Merge "New API to determine if device has lots of RAM."
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 3cec66f..0dba18b 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -106,6 +106,7 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/R/com/android/systemui/R.java)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java/android/media/IAudioService.P)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java/android/media/IAudioService.P)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/)
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/api/current.txt b/api/current.txt
index 9758433..c2c75bb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -184,21 +184,21 @@
   public static final class R.attr {
     ctor public R.attr();
     field public static final int absListViewStyle = 16842858; // 0x101006a
-    field public static final int accessibilityEventTypes = 16843644; // 0x101037c
-    field public static final int accessibilityFeedbackType = 16843646; // 0x101037e
-    field public static final int accessibilityFlags = 16843648; // 0x1010380
+    field public static final int accessibilityEventTypes = 16843643; // 0x101037b
+    field public static final int accessibilityFeedbackType = 16843645; // 0x101037d
+    field public static final int accessibilityFlags = 16843647; // 0x101037f
     field public static final int accountPreferences = 16843423; // 0x101029f
     field public static final int accountType = 16843407; // 0x101028f
     field public static final int action = 16842797; // 0x101002d
-    field public static final int actionBarDivider = 16843685; // 0x10103a5
-    field public static final int actionBarItemBackground = 16843686; // 0x10103a6
+    field public static final int actionBarDivider = 16843684; // 0x10103a4
+    field public static final int actionBarItemBackground = 16843685; // 0x10103a5
     field public static final int actionBarSize = 16843499; // 0x10102eb
-    field public static final int actionBarSplitStyle = 16843666; // 0x1010392
+    field public static final int actionBarSplitStyle = 16843665; // 0x1010391
     field public static final int actionBarStyle = 16843470; // 0x10102ce
     field public static final int actionBarTabBarStyle = 16843508; // 0x10102f4
     field public static final int actionBarTabStyle = 16843507; // 0x10102f3
     field public static final int actionBarTabTextStyle = 16843509; // 0x10102f5
-    field public static final int actionBarWidgetTheme = 16843681; // 0x10103a1
+    field public static final int actionBarWidgetTheme = 16843680; // 0x10103a0
     field public static final int actionButtonStyle = 16843480; // 0x10102d8
     field public static final int actionDropDownStyle = 16843479; // 0x10102d7
     field public static final int actionLayout = 16843515; // 0x10102fb
@@ -210,11 +210,11 @@
     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 = 16843642; // 0x101037a
-    field public static final int actionModeSplitBackground = 16843687; // 0x10103a7
-    field public static final int actionModeStyle = 16843678; // 0x101039e
+    field public static final int actionModeSelectAllDrawable = 16843641; // 0x1010379
+    field public static final int actionModeSplitBackground = 16843686; // 0x10103a6
+    field public static final int actionModeStyle = 16843677; // 0x101039d
     field public static final int actionOverflowButtonStyle = 16843510; // 0x10102f6
-    field public static final int actionProviderClass = 16843667; // 0x1010393
+    field public static final int actionProviderClass = 16843666; // 0x1010392
     field public static final int actionViewClass = 16843516; // 0x10102fc
     field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd
     field public static final int activityCloseEnterAnimation = 16842938; // 0x10100ba
@@ -226,7 +226,7 @@
     field public static final int alertDialogIcon = 16843605; // 0x1010355
     field public static final int alertDialogStyle = 16842845; // 0x101005d
     field public static final int alertDialogTheme = 16843529; // 0x1010309
-    field public static final int alignmentMode = 16843638; // 0x1010376
+    field public static final int alignmentMode = 16843637; // 0x1010375
     field public static final int allContactsName = 16843468; // 0x10102cc
     field public static final int allowBackup = 16843392; // 0x1010280
     field public static final int allowClearUserData = 16842757; // 0x1010005
@@ -260,8 +260,8 @@
     field public static final int background = 16842964; // 0x10100d4
     field public static final int backgroundDimAmount = 16842802; // 0x1010032
     field public static final int backgroundDimEnabled = 16843295; // 0x101021f
-    field public static final int backgroundSplit = 16843669; // 0x1010395
-    field public static final int backgroundStacked = 16843668; // 0x1010394
+    field public static final int backgroundSplit = 16843668; // 0x1010394
+    field public static final int backgroundStacked = 16843667; // 0x1010393
     field public static final int backupAgent = 16843391; // 0x101027f
     field public static final int baseline = 16843548; // 0x101031c
     field public static final int baselineAlignBottom = 16843042; // 0x1010122
@@ -270,7 +270,7 @@
     field public static final int borderlessButtonStyle = 16843563; // 0x101032b
     field public static final int bottom = 16843184; // 0x10101b0
     field public static final int bottomBright = 16842957; // 0x10100cd
-    field public static final int bottomChevronDrawable = 16843655; // 0x1010387
+    field public static final int bottomChevronDrawable = 16843654; // 0x1010386
     field public static final int bottomDark = 16842953; // 0x10100c9
     field public static final int bottomLeftRadius = 16843179; // 0x10101ab
     field public static final int bottomMedium = 16842958; // 0x10100ce
@@ -289,7 +289,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 = 16843649; // 0x1010381
+    field public static final int canRetrieveWindowContent = 16843648; // 0x1010380
     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
@@ -318,18 +318,18 @@
     field public static final int codes = 16843330; // 0x1010242
     field public static final int collapseColumns = 16843083; // 0x101014b
     field public static final int color = 16843173; // 0x10101a5
-    field public static final int colorActivatedHighlight = 16843674; // 0x101039a
+    field public static final int colorActivatedHighlight = 16843673; // 0x1010399
     field public static final int colorBackground = 16842801; // 0x1010031
     field public static final int colorBackgroundCacheHint = 16843435; // 0x10102ab
-    field public static final int colorFocusedHighlight = 16843673; // 0x1010399
+    field public static final int colorFocusedHighlight = 16843672; // 0x1010398
     field public static final int colorForeground = 16842800; // 0x1010030
     field public static final int colorForegroundInverse = 16843270; // 0x1010206
-    field public static final int colorLongPressedHighlight = 16843672; // 0x1010398
-    field public static final int colorMultiSelectHighlight = 16843675; // 0x101039b
-    field public static final int colorPressedHighlight = 16843671; // 0x1010397
-    field public static final int columnCount = 16843635; // 0x1010373
+    field public static final int colorLongPressedHighlight = 16843671; // 0x1010397
+    field public static final int colorMultiSelectHighlight = 16843674; // 0x101039a
+    field public static final int colorPressedHighlight = 16843670; // 0x1010396
+    field public static final int columnCount = 16843634; // 0x1010372
     field public static final int columnDelay = 16843215; // 0x10101cf
-    field public static final int columnOrderPreserved = 16843636; // 0x1010374
+    field public static final int columnOrderPreserved = 16843635; // 0x1010373
     field public static final int columnWidth = 16843031; // 0x1010117
     field public static final int compatibleWidthLimitDp = 16843621; // 0x1010365
     field public static final int completionHint = 16843122; // 0x1010172
@@ -383,11 +383,11 @@
     field public static final int drawSelectorOnTop = 16843004; // 0x10100fc
     field public static final int drawable = 16843161; // 0x1010199
     field public static final int drawableBottom = 16843118; // 0x101016e
-    field public static final int drawableEnd = 16843677; // 0x101039d
+    field public static final int drawableEnd = 16843676; // 0x101039c
     field public static final int drawableLeft = 16843119; // 0x101016f
     field public static final int drawablePadding = 16843121; // 0x1010171
     field public static final int drawableRight = 16843120; // 0x1010170
-    field public static final int drawableStart = 16843676; // 0x101039c
+    field public static final int drawableStart = 16843675; // 0x101039b
     field public static final int drawableTop = 16843117; // 0x101016d
     field public static final int drawingCacheQuality = 16842984; // 0x10100e8
     field public static final int dropDownAnchor = 16843363; // 0x1010263
@@ -444,7 +444,7 @@
     field public static final int fastScrollTextColor = 16843609; // 0x1010359
     field public static final int fastScrollThumbDrawable = 16843574; // 0x1010336
     field public static final int fastScrollTrackDrawable = 16843577; // 0x1010339
-    field public static final int feedbackCount = 16843661; // 0x101038d
+    field public static final int feedbackCount = 16843660; // 0x101038c
     field public static final int fillAfter = 16843197; // 0x10101bd
     field public static final int fillBefore = 16843196; // 0x10101bc
     field public static final int fillEnabled = 16843343; // 0x101024f
@@ -497,7 +497,7 @@
     field public static final int hand_hour = 16843011; // 0x1010103
     field public static final int hand_minute = 16843012; // 0x1010104
     field public static final int handle = 16843354; // 0x101025a
-    field public static final int handleDrawable = 16843651; // 0x1010383
+    field public static final int handleDrawable = 16843650; // 0x1010382
     field public static final int handleProfiling = 16842786; // 0x1010022
     field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
     field public static final int hardwareAccelerated = 16843475; // 0x10102d3
@@ -506,12 +506,12 @@
     field public static final int headerDividersEnabled = 16843310; // 0x101022e
     field public static final int height = 16843093; // 0x1010155
     field public static final int hint = 16843088; // 0x1010150
-    field public static final int hitRadius = 16843658; // 0x101038a
+    field public static final int hitRadius = 16843657; // 0x1010389
     field public static final int homeAsUpIndicator = 16843531; // 0x101030b
     field public static final int homeLayout = 16843549; // 0x101031d
     field public static final int horizontalDivider = 16843053; // 0x101012d
     field public static final int horizontalGap = 16843327; // 0x101023f
-    field public static final int horizontalOffset = 16843663; // 0x101038f
+    field public static final int horizontalOffset = 16843662; // 0x101038e
     field public static final int horizontalScrollViewStyle = 16843603; // 0x1010353
     field public static final int horizontalSpacing = 16843028; // 0x1010114
     field public static final int host = 16842792; // 0x1010028
@@ -557,7 +557,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 = 16843643; // 0x101037b
+    field public static final int isAuxiliary = 16843642; // 0x101037a
     field public static final int isDefault = 16843297; // 0x1010221
     field public static final int isIndicator = 16843079; // 0x1010147
     field public static final int isModifier = 16843334; // 0x1010246
@@ -610,7 +610,7 @@
     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 = 16843641; // 0x1010379
+    field public static final int layout_columnSpan = 16843640; // 0x1010378
     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
@@ -618,8 +618,8 @@
     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 = 16843639; // 0x1010377
-    field public static final int layout_rowSpan = 16843640; // 0x1010378
+    field public static final int layout_row = 16843638; // 0x1010376
+    field public static final int layout_rowSpan = 16843639; // 0x1010377
     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
@@ -629,7 +629,7 @@
     field public static final int layout_x = 16843135; // 0x101017f
     field public static final int layout_y = 16843136; // 0x1010180
     field public static final int left = 16843181; // 0x10101ad
-    field public static final int leftChevronDrawable = 16843652; // 0x1010384
+    field public static final int leftChevronDrawable = 16843651; // 0x1010383
     field public static final int lineSpacingExtra = 16843287; // 0x1010217
     field public static final int lineSpacingMultiplier = 16843288; // 0x1010218
     field public static final int lines = 16843092; // 0x1010154
@@ -641,8 +641,8 @@
     field public static final int listDividerAlertDialog = 16843525; // 0x1010305
     field public static final int listPopupWindowStyle = 16843519; // 0x10102ff
     field public static final int listPreferredItemHeight = 16842829; // 0x101004d
-    field public static final int listPreferredItemHeightLarge = 16843664; // 0x1010390
-    field public static final int listPreferredItemHeightSmall = 16843665; // 0x1010391
+    field public static final int listPreferredItemHeightLarge = 16843663; // 0x101038f
+    field public static final int listPreferredItemHeightSmall = 16843664; // 0x1010390
     field public static final int listSelector = 16843003; // 0x10100fb
     field public static final int listSeparatorTextViewStyle = 16843272; // 0x1010208
     field public static final int listViewStyle = 16842868; // 0x1010074
@@ -673,8 +673,8 @@
     field public static final int minHeight = 16843072; // 0x1010140
     field public static final int minLevel = 16843185; // 0x10101b1
     field public static final int minLines = 16843094; // 0x1010156
-    field public static final int minResizeHeight = 16843680; // 0x10103a0
-    field public static final int minResizeWidth = 16843679; // 0x101039f
+    field public static final int minResizeHeight = 16843679; // 0x101039f
+    field public static final int minResizeWidth = 16843678; // 0x101039e
     field public static final int minSdkVersion = 16843276; // 0x101020c
     field public static final int minWidth = 16843071; // 0x101013f
     field public static final int mode = 16843134; // 0x101017e
@@ -690,7 +690,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 = 16843647; // 0x101037f
+    field public static final int notificationTimeout = 16843646; // 0x101037e
     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
@@ -704,11 +704,11 @@
     field public static final int orderingFromXml = 16843239; // 0x10101e7
     field public static final int orientation = 16842948; // 0x10100c4
     field public static final int outAnimation = 16843128; // 0x1010178
-    field public static final int outerRadius = 16843657; // 0x1010389
+    field public static final int outerRadius = 16843656; // 0x1010388
     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 = 16843645; // 0x101037d
+    field public static final int packageNames = 16843644; // 0x101037c
     field public static final int padding = 16842965; // 0x10100d5
     field public static final int paddingBottom = 16842969; // 0x10100d9
     field public static final int paddingLeft = 16842966; // 0x10100d6
@@ -793,17 +793,17 @@
     field public static final int restoreAnyVersion = 16843450; // 0x10102ba
     field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
     field public static final int right = 16843183; // 0x10101af
-    field public static final int rightChevronDrawable = 16843653; // 0x1010385
+    field public static final int rightChevronDrawable = 16843652; // 0x1010384
     field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093
     field public static final int ringtoneType = 16843257; // 0x10101f9
     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 = 16843633; // 0x1010371
+    field public static final int rowCount = 16843632; // 0x1010370
     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 = 16843634; // 0x1010372
+    field public static final int rowOrderPreserved = 16843633; // 0x1010371
     field public static final int saveEnabled = 16842983; // 0x10100e7
     field public static final int scaleGravity = 16843262; // 0x10101fe
     field public static final int scaleHeight = 16843261; // 0x10101fd
@@ -869,7 +869,7 @@
     field public static final int smallIcon = 16843422; // 0x101029e
     field public static final int smallScreens = 16843396; // 0x1010284
     field public static final int smoothScrollbar = 16843313; // 0x1010231
-    field public static final int snapMargin = 16843660; // 0x101038c
+    field public static final int snapMargin = 16843659; // 0x101038b
     field public static final int soundEffectsEnabled = 16843285; // 0x1010215
     field public static final int spacing = 16843027; // 0x1010113
     field public static final int spinnerDropDownItemStyle = 16842887; // 0x1010087
@@ -915,11 +915,10 @@
     field public static final int stretchMode = 16843030; // 0x1010116
     field public static final int subtitle = 16843473; // 0x10102d1
     field public static final int subtitleTextStyle = 16843513; // 0x10102f9
-    field public static final int subtypeExtraValue = 16843684; // 0x10103a4
-    field public static final int subtypeLocale = 16843683; // 0x10103a3
+    field public static final int subtypeExtraValue = 16843683; // 0x10103a3
+    field public static final int subtypeLocale = 16843682; // 0x10103a2
     field public static final int suggestActionMsg = 16843228; // 0x10101dc
     field public static final int suggestActionMsgColumn = 16843229; // 0x10101dd
-    field public static final int suggestionsEnabled = 16843632; // 0x1010370
     field public static final int summary = 16843241; // 0x10101e9
     field public static final int summaryColumn = 16843426; // 0x10102a2
     field public static final int summaryOff = 16843248; // 0x10101f0
@@ -936,7 +935,7 @@
     field public static final int tag = 16842961; // 0x10100d1
     field public static final int targetActivity = 16843266; // 0x1010202
     field public static final int targetClass = 16842799; // 0x101002f
-    field public static final int targetDrawables = 16843650; // 0x1010382
+    field public static final int targetDrawables = 16843649; // 0x1010381
     field public static final int targetPackage = 16842785; // 0x1010021
     field public static final int targetSdkVersion = 16843376; // 0x1010270
     field public static final int taskAffinity = 16842770; // 0x1010012
@@ -951,15 +950,15 @@
     field public static final int tension = 16843370; // 0x101026a
     field public static final int testOnly = 16843378; // 0x1010272
     field public static final int text = 16843087; // 0x101014f
-    field public static final int textAllCaps = 16843670; // 0x1010396
+    field public static final int textAllCaps = 16843669; // 0x1010395
     field public static final int textAppearance = 16842804; // 0x1010034
     field public static final int textAppearanceButton = 16843271; // 0x1010207
     field public static final int textAppearanceInverse = 16842805; // 0x1010035
     field public static final int textAppearanceLarge = 16842816; // 0x1010040
     field public static final int textAppearanceLargeInverse = 16842819; // 0x1010043
     field public static final int textAppearanceLargePopupMenu = 16843521; // 0x1010301
-    field public static final int textAppearanceListItem = 16843688; // 0x10103a8
-    field public static final int textAppearanceListItemSmall = 16843689; // 0x10103a9
+    field public static final int textAppearanceListItem = 16843687; // 0x10103a7
+    field public static final int textAppearanceListItemSmall = 16843688; // 0x10103a8
     field public static final int textAppearanceMedium = 16842817; // 0x1010041
     field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044
     field public static final int textAppearanceSearchResultSubtitle = 16843424; // 0x10102a0
@@ -1027,7 +1026,7 @@
     field public static final int toYScale = 16843205; // 0x10101c5
     field public static final int top = 16843182; // 0x10101ae
     field public static final int topBright = 16842955; // 0x10100cb
-    field public static final int topChevronDrawable = 16843654; // 0x1010386
+    field public static final int topChevronDrawable = 16843653; // 0x1010385
     field public static final int topDark = 16842951; // 0x10100c7
     field public static final int topLeftRadius = 16843177; // 0x10101a9
     field public static final int topOffset = 16843352; // 0x1010258
@@ -1039,12 +1038,12 @@
     field public static final int translationY = 16843555; // 0x1010323
     field public static final int type = 16843169; // 0x10101a1
     field public static final int typeface = 16842902; // 0x1010096
-    field public static final int uiOptions = 16843682; // 0x10103a2
+    field public static final int uiOptions = 16843681; // 0x10103a1
     field public static final int uncertainGestureColor = 16843382; // 0x1010276
     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 = 16843637; // 0x1010375
+    field public static final int useDefaultMargins = 16843636; // 0x1010374
     field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
     field public static final int useLevel = 16843167; // 0x101019f
     field public static final int userVisible = 16843409; // 0x1010291
@@ -1058,10 +1057,10 @@
     field public static final int verticalCorrection = 16843322; // 0x101023a
     field public static final int verticalDivider = 16843054; // 0x101012e
     field public static final int verticalGap = 16843328; // 0x1010240
-    field public static final int verticalOffset = 16843662; // 0x101038e
+    field public static final int verticalOffset = 16843661; // 0x101038d
     field public static final int verticalScrollbarPosition = 16843572; // 0x1010334
     field public static final int verticalSpacing = 16843029; // 0x1010115
-    field public static final int vibrationDuration = 16843659; // 0x101038b
+    field public static final int vibrationDuration = 16843658; // 0x101038a
     field public static final int visibility = 16842972; // 0x10100dc
     field public static final int visible = 16843156; // 0x1010194
     field public static final int vmSafeMode = 16843448; // 0x10102b8
@@ -1078,7 +1077,7 @@
     field public static final int wallpaperIntraOpenExitAnimation = 16843416; // 0x1010298
     field public static final int wallpaperOpenEnterAnimation = 16843411; // 0x1010293
     field public static final int wallpaperOpenExitAnimation = 16843412; // 0x1010294
-    field public static final int waveDrawable = 16843656; // 0x1010388
+    field public static final int waveDrawable = 16843655; // 0x1010387
     field public static final int webTextViewStyle = 16843449; // 0x10102b9
     field public static final int webViewStyle = 16842885; // 0x1010085
     field public static final int weekDayTextAppearance = 16843592; // 0x1010348
@@ -8721,7 +8720,6 @@
 
   public class SurfaceTexture {
     ctor public SurfaceTexture(int);
-    ctor public SurfaceTexture(int, boolean);
     method public long getTimestamp();
     method public void getTransformMatrix(float[]);
     method public void release();
@@ -13975,6 +13973,7 @@
   }
 
   public final class GLUtils {
+    method public static java.lang.String getEGLErrorString(int);
     method public static int getInternalFormat(android.graphics.Bitmap);
     method public static int getType(android.graphics.Bitmap);
     method public static void texImage2D(int, int, int, android.graphics.Bitmap, int);
@@ -20811,6 +20810,7 @@
     method public java.lang.String getLocale();
     method public int getSpanTypeId();
     method public java.lang.String[] getSuggestions();
+    method public void setFlags(int);
     method public void updateDrawState(android.text.TextPaint);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final java.lang.String ACTION_SUGGESTION_PICKED = "android.text.style.SUGGESTION_PICKED";
@@ -27131,7 +27131,6 @@
     method public void setSingleLine();
     method public void setSingleLine(boolean);
     method public final void setSpannableFactory(android.text.Spannable.Factory);
-    method public void setSuggestionsEnabled(boolean);
     method public final void setText(java.lang.CharSequence);
     method public void setText(java.lang.CharSequence, android.widget.TextView.BufferType);
     method public final void setText(char[], int, int);
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index 836cf15..0b08c5e 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -80,6 +80,9 @@
     dump_file("SLAB INFO", "/proc/slabinfo");
     dump_file("ZONEINFO", "/proc/zoneinfo");
     dump_file("PAGETYPEINFO", "/proc/pagetypeinfo");
+    dump_file("BUDDYINFO", "/proc/buddyinfo");
+    dump_file("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
+    run_command("QTAGUID STATS INFO", 10, "su", "root", "cat", "/proc/net/xt_qtaguid/stats", NULL);
 
     if (screenshot_path[0]) {
         LOGI("taking screenshot\n");
@@ -114,8 +117,10 @@
     dump_file("NETWORK ROUTES", "/proc/net/route");
     dump_file("NETWORK ROUTES IPV6", "/proc/net/ipv6_route");
     dump_file("ARP CACHE", "/proc/net/arp");
-    run_command("IPTABLES", 10, "su", "root", "iptables", "-L", "-n", NULL);
+    run_command("IPTABLES", 10, "su", "root", "iptables", "-L", "-nvx", NULL);
+    run_command("IP6TABLES", 10, "su", "root", "ip6tables", "-L", "-nvx", NULL);
     run_command("IPTABLE NAT", 10, "su", "root", "iptables", "-t", "nat", "-L", "-n", NULL);
+    run_command("IPT6ABLE NAT", 10, "su", "root", "ip6tables", "-t", "nat", "-L", "-n", NULL);
 
     run_command("WIFI NETWORKS", 20,
             "su", "root", "wpa_cli", "list_networks", NULL);
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index ff04757..f81ea81 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -107,19 +107,22 @@
         private final int mHeight;
         private int mDrawLeft;
         private int mDrawTop;
+        private final Paint mPaint;
 
         private FastBitmapDrawable(Bitmap bitmap) {
             mBitmap = bitmap;
             mWidth = bitmap.getWidth();
             mHeight = bitmap.getHeight();
+
             setBounds(0, 0, mWidth, mHeight);
+
+            mPaint = new Paint();
+            mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
         }
 
         @Override
         public void draw(Canvas canvas) {
-            Paint paint = new Paint();
-            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
-            canvas.drawBitmap(mBitmap, mDrawLeft, mDrawTop, paint);
+            canvas.drawBitmap(mBitmap, mDrawLeft, mDrawTop, mPaint);
         }
 
         @Override
@@ -134,33 +137,23 @@
         }
 
         @Override
-        public void setBounds(Rect bounds) {
-            // TODO Auto-generated method stub
-            super.setBounds(bounds);
-        }
-
-        @Override
         public void setAlpha(int alpha) {
-            throw new UnsupportedOperationException(
-                    "Not supported with this drawable");
+            throw new UnsupportedOperationException("Not supported with this drawable");
         }
 
         @Override
         public void setColorFilter(ColorFilter cf) {
-            throw new UnsupportedOperationException(
-                    "Not supported with this drawable");
+            throw new UnsupportedOperationException("Not supported with this drawable");
         }
 
         @Override
         public void setDither(boolean dither) {
-            throw new UnsupportedOperationException(
-                    "Not supported with this drawable");
+            throw new UnsupportedOperationException("Not supported with this drawable");
         }
 
         @Override
         public void setFilterBitmap(boolean filter) {
-            throw new UnsupportedOperationException(
-                    "Not supported with this drawable");
+            throw new UnsupportedOperationException("Not supported with this drawable");
         }
 
         @Override
@@ -230,7 +223,7 @@
                 }
                 mWallpaper = null;
                 try {
-                    mWallpaper = getCurrentWallpaperLocked(context);
+                    mWallpaper = getCurrentWallpaperLocked();
                 } catch (OutOfMemoryError e) {
                     Log.w(TAG, "No memory load current wallpaper", e);
                 }
@@ -253,7 +246,7 @@
             }
         }
 
-        private Bitmap getCurrentWallpaperLocked(Context context) {
+        private Bitmap getCurrentWallpaperLocked() {
             try {
                 Bundle params = new Bundle();
                 ParcelFileDescriptor fd = mService.getWallpaper(this, params);
@@ -265,17 +258,19 @@
                         BitmapFactory.Options options = new BitmapFactory.Options();
                         Bitmap bm = BitmapFactory.decodeFileDescriptor(
                                 fd.getFileDescriptor(), null, options);
-                        return generateBitmap(context, bm, width, height);
+                        return generateBitmap(bm, width, height);
                     } catch (OutOfMemoryError e) {
                         Log.w(TAG, "Can't decode file", e);
                     } finally {
                         try {
                             fd.close();
                         } catch (IOException e) {
+                            // Ignore
                         }
                     }
                 }
             } catch (RemoteException e) {
+                // Ignore
             }
             return null;
         }
@@ -291,27 +286,29 @@
                     try {
                         BitmapFactory.Options options = new BitmapFactory.Options();
                         Bitmap bm = BitmapFactory.decodeStream(is, null, options);
-                        return generateBitmap(context, bm, width, height);
+                        return generateBitmap(bm, width, height);
                     } catch (OutOfMemoryError e) {
                         Log.w(TAG, "Can't decode stream", e);
                     } finally {
                         try {
                             is.close();
                         } catch (IOException e) {
+                            // Ignore
                         }
                     }
                 }
             } catch (RemoteException e) {
+                // Ignore
             }
             return null;
         }
     }
     
-    private static Object mSync = new Object();
+    private static final Object sSync = new Object[0];
     private static Globals sGlobals;
 
     static void initGlobals(Looper looper) {
-        synchronized (mSync) {
+        synchronized (sSync) {
             if (sGlobals == null) {
                 sGlobals = new Globals(looper);
             }
@@ -390,8 +387,7 @@
     public Drawable getFastDrawable() {
         Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true);
         if (bm != null) {
-            Drawable dr = new FastBitmapDrawable(bm);
-            return dr;
+            return new FastBitmapDrawable(bm);
         }
         return null;
     }
@@ -406,13 +402,21 @@
     public Drawable peekFastDrawable() {
         Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false);
         if (bm != null) {
-            Drawable dr = new FastBitmapDrawable(bm);
-            return dr;
+            return new FastBitmapDrawable(bm);
         }
         return null;
     }
 
     /**
+     * Like {@link #getDrawable()} but returns a Bitmap.
+     * 
+     * @hide
+     */
+    public Bitmap getBitmap() {
+        return sGlobals.peekWallpaperBitmap(mContext, true);
+    }
+
+    /**
      * Remove all internal references to the last loaded wallpaper.  Useful
      * for apps that want to reduce memory usage when they only temporarily
      * need to have the wallpaper.  After calling, the next request for the
@@ -464,6 +468,7 @@
                 }
             }
         } catch (RemoteException e) {
+            // Ignore
         }
     }
     
@@ -493,6 +498,7 @@
                 }
             }
         } catch (RemoteException e) {
+            // Ignore
         }
     }
 
@@ -524,6 +530,7 @@
                 }
             }
         } catch (RemoteException e) {
+            // Ignore
         }
     }
 
@@ -594,6 +601,7 @@
         try {
             sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight);
         } catch (RemoteException e) {
+            // Ignore
         }
     }
     
@@ -690,7 +698,7 @@
         setResource(com.android.internal.R.drawable.default_wallpaper);
     }
     
-    static Bitmap generateBitmap(Context context, Bitmap bm, int width, int height) {
+    static Bitmap generateBitmap(Bitmap bm, int width, int height) {
         if (bm == null) {
             return null;
         }
@@ -717,7 +725,7 @@
 
             if (deltaw > 0 || deltah > 0) {
                 // We need to scale up so it covers the entire area.
-                float scale = 1.0f;
+                float scale;
                 if (deltaw > deltah) {
                     scale = width / (float)targetRect.right;
                 } else {
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index a41a330..bc45945 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -2328,7 +2328,7 @@
          * <p>The reference code is as follows.
          *
 	 * <pre>
-         * public void public void onOrientationChanged(int orientation) {
+         * public void onOrientationChanged(int orientation) {
          *     if (orientation == ORIENTATION_UNKNOWN) return;
          *     android.hardware.Camera.CameraInfo info =
          *            new android.hardware.Camera.CameraInfo();
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index a66fa81..55f3b4a 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -2692,6 +2692,7 @@
                 ContentValues cv = new ContentValues();
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ACCOUNT_NAME);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ACCOUNT_TYPE);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, DATA_SET);
                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, _ID);
                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY);
                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, VERSION);
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 6bde802..5fed775 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -16,21 +16,18 @@
 
 package android.text;
 
-import com.android.internal.util.ArrayUtils;
-
 import android.graphics.Canvas;
 import android.graphics.Paint;
-import android.text.style.SuggestionSpan;
+
+import com.android.internal.util.ArrayUtils;
 
 import java.lang.reflect.Array;
 
 /**
  * This is the class for text whose content and markup can both be changed.
  */
-public class SpannableStringBuilder
-implements CharSequence, GetChars, Spannable, Editable, Appendable,
-           GraphicsOperations
-{
+public class SpannableStringBuilder implements CharSequence, GetChars, Spannable, Editable,
+        Appendable, GraphicsOperations {
     /**
      * Create a new SpannableStringBuilder with empty contents
      */
@@ -111,8 +108,7 @@
         if (where < 0) {
             throw new IndexOutOfBoundsException("charAt: " + where + " < 0");
         } else if (where >= len) {
-            throw new IndexOutOfBoundsException("charAt: " + where +
-                                                " >= length " + len);
+            throw new IndexOutOfBoundsException("charAt: " + where + " >= length " + len);
         }
 
         if (where >= mGapStart)
@@ -266,8 +262,7 @@
         return append(String.valueOf(text));
     }
 
-    private int change(int start, int end,
-                       CharSequence tb, int tbstart, int tbend) {
+    private int change(int start, int end, CharSequence tb, int tbstart, int tbend) {
         return change(true, start, end, tb, tbstart, tbend);
     }
 
@@ -277,8 +272,9 @@
         int ret = tbend - tbstart;
         TextWatcher[] recipients = null;
 
-        if (notify)
+        if (notify) {
             recipients = sendTextWillChange(start, end - start, tbend - tbstart);
+        }
 
         for (int i = mSpanCount - 1; i >= 0; i--) {
             if ((mSpanFlags[i] & SPAN_PARAGRAPH) == SPAN_PARAGRAPH) {
@@ -353,7 +349,6 @@
         // no need for span fixup on pure insertion
         if (tbend > tbstart && end - start == 0) {
             if (notify) {
-                removeSuggestionSpans(start, end);
                 sendTextChange(recipients, start, end - start, tbend - tbstart);
                 sendTextHasChanged(recipients);
             }
@@ -388,7 +383,6 @@
             if (mSpanEnds[i] < mSpanStarts[i]) {
                 removeSpan(i);
             }
-            removeSuggestionSpans(start, end);
         }
 
         if (notify) {
@@ -399,30 +393,26 @@
         return ret;
     }
 
-    /**
-     * Removes the SuggestionSpan that overlap the [start, end] range, and that would
-     * not make sense anymore after the change.
-     */
-    private void removeSuggestionSpans(int start, int end) {
-        for (int i = mSpanCount - 1; i >= 0; i--) {
-            final int spanEnd = mSpanEnds[i];
-            final int spanSpart = mSpanStarts[i];
-            if ((mSpans[i] instanceof SuggestionSpan) && (
-                    (spanSpart < start && spanEnd > start) ||
-                    (spanSpart < end && spanEnd > end))) {
-                removeSpan(i);
-            }
-        }
-    }
-
     private void removeSpan(int i) {
-        // XXX send notification on removal
-        System.arraycopy(mSpans, i + 1, mSpans, i, mSpanCount - (i + 1));
-        System.arraycopy(mSpanStarts, i + 1, mSpanStarts, i, mSpanCount - (i + 1));
-        System.arraycopy(mSpanEnds, i + 1, mSpanEnds, i, mSpanCount - (i + 1));
-        System.arraycopy(mSpanFlags, i + 1, mSpanFlags, i, mSpanCount - (i + 1));
+        Object object = mSpans[i];
+
+        int start = mSpanStarts[i];
+        int end = mSpanEnds[i];
+
+        if (start > mGapStart) start -= mGapLength;
+        if (end > mGapStart) end -= mGapLength;
+
+        int count = mSpanCount - (i + 1);
+        System.arraycopy(mSpans, i + 1, mSpans, i, count);
+        System.arraycopy(mSpanStarts, i + 1, mSpanStarts, i, count);
+        System.arraycopy(mSpanEnds, i + 1, mSpanEnds, i, count);
+        System.arraycopy(mSpanFlags, i + 1, mSpanFlags, i, count);
 
         mSpanCount--;
+
+        mSpans[mSpanCount] = null;
+
+        sendSpanRemoved(object, start, end);
     }
 
     // Documentation from interface
@@ -462,11 +452,10 @@
             moveGapTo(end);
             TextWatcher[] recipients;
 
-            recipients = sendTextWillChange(start, end - start,
-                                            tbend - tbstart);
-
             int origlen = end - start;
 
+            recipients = sendTextWillChange(start, origlen, tbend - tbstart);
+
             if (mGapLength < 2)
                 resizeFor(length() + 1);
 
@@ -486,11 +475,9 @@
                 new Exception("mGapLength < 1").printStackTrace();
             }
 
-            int oldlen = (end + 1) - start;
-
             int inserted = change(false, start + 1, start + 1, tb, tbstart, tbend);
             change(false, start, start + 1, "", 0, 0);
-            change(false, start + inserted, start + inserted + oldlen - 1, "", 0, 0);
+            change(false, start + inserted, start + inserted + origlen, "", 0, 0);
 
             /*
              * Special case to keep the cursor in the same position
@@ -515,13 +502,12 @@
                 off = off * inserted / (end - start);
                 selend = (int) off + start;
 
-                setSpan(false, Selection.SELECTION_END, selend, selend,
-                        Spanned.SPAN_POINT_POINT);
+                setSpan(false, Selection.SELECTION_END, selend, selend, Spanned.SPAN_POINT_POINT);
             }
-
             sendTextChange(recipients, start, origlen, inserted);
             sendTextHasChanged(recipients);
         }
+
         return this; 
     }
 
@@ -534,8 +520,7 @@
         setSpan(true, what, start, end, flags);
     }
 
-    private void setSpan(boolean send,
-                         Object what, int start, int end, int flags) {
+    private void setSpan(boolean send, Object what, int start, int end, int flags) {
         int nstart = start;
         int nend = end;
 
@@ -546,8 +531,7 @@
                 char c = charAt(start - 1);
 
                 if (c != '\n')
-                    throw new RuntimeException(
-                            "PARAGRAPH span must start at paragraph boundary");
+                    throw new RuntimeException("PARAGRAPH span must start at paragraph boundary");
             }
         }
 
@@ -556,23 +540,22 @@
                 char c = charAt(end - 1);
 
                 if (c != '\n')
-                    throw new RuntimeException(
-                            "PARAGRAPH span must end at paragraph boundary");
+                    throw new RuntimeException("PARAGRAPH span must end at paragraph boundary");
             }
         }
 
-        if (start > mGapStart)
+        if (start > mGapStart) {
             start += mGapLength;
-        else if (start == mGapStart) {
+        } else if (start == mGapStart) {
             int flag = (flags & START_MASK) >> START_SHIFT;
 
             if (flag == POINT || (flag == PARAGRAPH && start == length()))
                 start += mGapLength;
         }
 
-        if (end > mGapStart)
+        if (end > mGapStart) {
             end += mGapLength;
-        else if (end == mGapStart) {
+        } else if (end == mGapStart) {
             int flag = (flags & END_MASK);
 
             if (flag == POINT || (flag == PARAGRAPH && end == length()))
@@ -637,25 +620,7 @@
     public void removeSpan(Object what) {
         for (int i = mSpanCount - 1; i >= 0; i--) {
             if (mSpans[i] == what) {
-                int ostart = mSpanStarts[i];
-                int oend = mSpanEnds[i];
-
-                if (ostart > mGapStart)
-                    ostart -= mGapLength;
-                if (oend > mGapStart)
-                    oend -= mGapLength;
-
-                int count = mSpanCount - (i + 1);
-
-                System.arraycopy(mSpans, i + 1, mSpans, i, count);
-                System.arraycopy(mSpanStarts, i + 1, mSpanStarts, i, count);
-                System.arraycopy(mSpanEnds, i + 1, mSpanEnds, i, count);
-                System.arraycopy(mSpanFlags, i + 1, mSpanFlags, i, count);
-
-                mSpanCount--;
-                mSpans[mSpanCount] = null;
-
-                sendSpanRemoved(what, ostart, oend);
+                removeSpan(i);
                 return;
             }
         }
@@ -729,6 +694,8 @@
      */
     @SuppressWarnings("unchecked")
     public <T> T[] getSpans(int queryStart, int queryEnd, Class<T> kind) {
+        if (kind == null) return ArrayUtils.emptyArray(kind);
+
         int spanCount = mSpanCount;
         Object[] spans = mSpans;
         int[] starts = mSpanStarts;
@@ -742,6 +709,8 @@
         T ret1 = null;
 
         for (int i = 0; i < spanCount; i++) {
+            if (!kind.isInstance(spans[i])) continue;
+
             int spanStart = starts[i];
             int spanEnd = ends[i];
 
@@ -766,10 +735,6 @@
                     continue;
             }
 
-            if (kind != null && !kind.isInstance(spans[i])) {
-                continue;
-            }
-
             if (count == 0) {
                 // Safe conversion thanks to the isInstance test above
                 ret1 = (T) spans[i];
@@ -909,8 +874,7 @@
         return recip;
     }
 
-    private void sendTextChange(TextWatcher[] recip, int start, int before,
-                                int after) {
+    private void sendTextChange(TextWatcher[] recip, int start, int before, int after) {
         int n = recip.length;
 
         for (int i = 0; i < n; i++) {
@@ -945,8 +909,7 @@
     }
 
     private void sendSpanChanged(Object what, int s, int e, int st, int en) {
-        SpanWatcher[] recip = getSpans(Math.min(s, st), Math.max(e, en),
-                                  SpanWatcher.class);
+        SpanWatcher[] recip = getSpans(Math.min(s, st), Math.max(e, en), SpanWatcher.class);
         int n = recip.length;
 
         for (int i = 0; i < n; i++) {
diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java
index af524ee..0433ec4 100644
--- a/core/java/android/text/method/WordIterator.java
+++ b/core/java/android/text/method/WordIterator.java
@@ -73,6 +73,10 @@
         }
     };
 
+    public void forceUpdate() {
+        mCurrentDirty = true;
+    }
+
     public void setCharSequence(CharSequence incoming) {
         // When incoming is different object, move listeners to new sequence
         // and mark as dirty so we reload contents.
diff --git a/core/java/android/text/style/SpellCheckSpan.java b/core/java/android/text/style/SpellCheckSpan.java
new file mode 100644
index 0000000..9b23177
--- /dev/null
+++ b/core/java/android/text/style/SpellCheckSpan.java
@@ -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.text.style;
+
+/**
+ * A SpellCheckSpan is an internal data structure created by the TextView's SpellChecker to
+ * annotate portions of the text that are about to or currently being spell checked. They are
+ * automatically removed once the spell check is completed.
+ *
+ * @hide
+ */
+public class SpellCheckSpan {
+
+    private boolean mSpellCheckInProgress;
+
+    public SpellCheckSpan() {
+        mSpellCheckInProgress = false;
+    }
+
+    public void setSpellCheckInProgress() {
+        mSpellCheckInProgress = true;
+    }
+
+    public boolean isSpellCheckInProgress() {
+        return mSpellCheckInProgress;
+    }
+}
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index fb94bc7..ea57f91 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -40,7 +40,7 @@
  * These spans should typically be created by the input method to provide correction and alternates
  * for the text.
  *
- * @see TextView#setSuggestionsEnabled(boolean)
+ * @see TextView#isSuggestionsEnabled()
  */
 public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
 
@@ -76,7 +76,7 @@
      * And the current IME might want to specify any IME as the target IME including other IMEs.
      */
 
-    private final int mFlags;
+    private int mFlags;
     private final String[] mSuggestions;
     private final String mLocaleString;
     private final String mNotificationTargetClassName;
@@ -134,8 +134,7 @@
         } else {
             mNotificationTargetClassName = "";
         }
-        mHashCode = hashCodeInternal(
-                mFlags, mSuggestions, mLocaleString, mNotificationTargetClassName);
+        mHashCode = hashCodeInternal(mSuggestions, mLocaleString, mNotificationTargetClassName);
 
         initStyle(context);
     }
@@ -211,6 +210,10 @@
         return mFlags;
     }
 
+    public void setFlags(int flags) {
+        mFlags = flags;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -247,10 +250,10 @@
         return mHashCode;
     }
 
-    private static int hashCodeInternal(int flags, String[] suggestions,String locale,
+    private static int hashCodeInternal(String[] suggestions, String locale,
             String notificationTargetClassName) {
-        return Arrays.hashCode(new Object[] {SystemClock.uptimeMillis(), flags, suggestions, locale,
-                notificationTargetClassName});
+        return Arrays.hashCode(new Object[] {Long.valueOf(SystemClock.uptimeMillis()), suggestions,
+                locale, notificationTargetClassName});
     }
 
     public static final Parcelable.Creator<SuggestionSpan> CREATOR =
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index a05637d..926d424 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -21,6 +21,7 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
+import android.opengl.GLUtils;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.util.Log;
@@ -402,51 +403,6 @@
         }
 
         /**
-         * Return a string for the EGL error code, or the hex representation
-         * if the error is unknown.
-         * 
-         * @param error The EGL error to convert into a String.
-         * 
-         * @return An error string correponding to the EGL error code.
-         */
-        static String getEGLErrorString(int error) {
-            switch (error) {
-                case EGL_SUCCESS:
-                    return "EGL_SUCCESS";
-                case EGL_NOT_INITIALIZED:
-                    return "EGL_NOT_INITIALIZED";
-                case EGL_BAD_ACCESS:
-                    return "EGL_BAD_ACCESS";
-                case EGL_BAD_ALLOC:
-                    return "EGL_BAD_ALLOC";
-                case EGL_BAD_ATTRIBUTE:
-                    return "EGL_BAD_ATTRIBUTE";
-                case EGL_BAD_CONFIG:
-                    return "EGL_BAD_CONFIG";
-                case EGL_BAD_CONTEXT:
-                    return "EGL_BAD_CONTEXT";
-                case EGL_BAD_CURRENT_SURFACE:
-                    return "EGL_BAD_CURRENT_SURFACE";
-                case EGL_BAD_DISPLAY:
-                    return "EGL_BAD_DISPLAY";
-                case EGL_BAD_MATCH:
-                    return "EGL_BAD_MATCH";
-                case EGL_BAD_NATIVE_PIXMAP:
-                    return "EGL_BAD_NATIVE_PIXMAP";
-                case EGL_BAD_NATIVE_WINDOW:
-                    return "EGL_BAD_NATIVE_WINDOW";
-                case EGL_BAD_PARAMETER:
-                    return "EGL_BAD_PARAMETER";
-                case EGL_BAD_SURFACE:
-                    return "EGL_BAD_SURFACE";
-                case EGL11.EGL_CONTEXT_LOST:
-                    return "EGL_CONTEXT_LOST";
-                default:
-                    return "0x" + Integer.toHexString(error);
-            }
-        }
-
-        /**
          * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)}
          * is invoked and the requested flag is turned off. The error code is
          * also logged as a warning.
@@ -458,7 +414,7 @@
                     // something bad has happened revert to
                     // normal rendering.
                     fallback(error != EGL11.EGL_CONTEXT_LOST);
-                    Log.w(LOG_TAG, "EGL error: " + getEGLErrorString(error));
+                    Log.w(LOG_TAG, "EGL error: " + GLUtils.getEGLErrorString(error));
                 }
             }
         }
@@ -523,14 +479,14 @@
                     
                     if (sEglDisplay == EGL_NO_DISPLAY) {
                         throw new RuntimeException("eglGetDisplay failed "
-                                + getEGLErrorString(sEgl.eglGetError()));
+                                + GLUtils.getEGLErrorString(sEgl.eglGetError()));
                     }
                     
                     // We can now initialize EGL for that display
                     int[] version = new int[2];
                     if (!sEgl.eglInitialize(sEglDisplay, version)) {
                         throw new RuntimeException("eglInitialize failed " +
-                                getEGLErrorString(sEgl.eglGetError()));
+                                GLUtils.getEGLErrorString(sEgl.eglGetError()));
                     }
         
                     sEglConfig = chooseEglConfig();
@@ -579,7 +535,7 @@
 
             if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) {
                 throw new IllegalArgumentException("eglChooseConfig failed " +
-                        getEGLErrorString(sEgl.eglGetError()));
+                        GLUtils.getEGLErrorString(sEgl.eglGetError()));
             } else if (configsCount[0] > 0) {
                 if ("choice".equalsIgnoreCase(debug)) {
                     printConfig(configs[0]);
@@ -647,7 +603,7 @@
              */
             if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
                 throw new Surface.OutOfResourcesException("eglMakeCurrent failed "
-                        + getEGLErrorString(sEgl.eglGetError()));
+                        + GLUtils.getEGLErrorString(sEgl.eglGetError()));
             }
 
             // If mDirtyRegions is set, this means we have an EGL configuration
@@ -734,7 +690,7 @@
                     return false;
                 }
                 throw new RuntimeException("createWindowSurface failed "
-                        + getEGLErrorString(error));
+                        + GLUtils.getEGLErrorString(error));
             }
             return true;
         }
@@ -856,7 +812,7 @@
                 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
                     fallback(true);
                     Log.e(LOG_TAG, "eglMakeCurrent failed " +
-                            getEGLErrorString(sEgl.eglGetError()));
+                            GLUtils.getEGLErrorString(sEgl.eglGetError()));
                     return SURFACE_STATE_ERROR;
                 } else {
                     return SURFACE_STATE_UPDATED;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f23c366..357b6e4 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -879,6 +879,8 @@
             host.dispatchAttachedToWindow(attachInfo, 0);
             //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
 
+            host.fitSystemWindows(mAttachInfo.mContentInsets);
+
         } else {
             desiredWindowWidth = frame.width();
             desiredWindowHeight = frame.height();
@@ -914,85 +916,15 @@
             final Resources res = mView.getContext().getResources();
 
             if (mFirst) {
-                host.fitSystemWindows(mAttachInfo.mContentInsets);
                 // make sure touch mode code executes by setting cached value
                 // to opposite of the added touch mode.
                 mAttachInfo.mInTouchMode = !mAddedTouchMode;
                 ensureTouchModeLocally(mAddedTouchMode);
             } else {
-                if (!mAttachInfo.mContentInsets.equals(mPendingContentInsets)) {
-                    if (mWidth > 0 && mHeight > 0 &&
-                            mSurface != null && mSurface.isValid() &&
-                            !mAttachInfo.mTurnOffWindowResizeAnim &&
-                            mAttachInfo.mHardwareRenderer != null &&
-                            mAttachInfo.mHardwareRenderer.isEnabled() &&
-                            mAttachInfo.mHardwareRenderer.validate() &&
-                            lp != null && !PixelFormat.formatHasAlpha(lp.format)) {
-
-                        disposeResizeBuffer();
-
-                        boolean completed = false;
-                        HardwareCanvas canvas = null;
-                        try {
-                            if (mResizeBuffer == null) {
-                                mResizeBuffer = mAttachInfo.mHardwareRenderer.createHardwareLayer(
-                                        mWidth, mHeight, false);
-                            } else if (mResizeBuffer.getWidth() != mWidth ||
-                                    mResizeBuffer.getHeight() != mHeight) {
-                                mResizeBuffer.resize(mWidth, mHeight);
-                            }
-                            canvas = mResizeBuffer.start(mAttachInfo.mHardwareCanvas);
-                            canvas.setViewport(mWidth, mHeight);
-                            canvas.onPreDraw(null);
-                            final int restoreCount = canvas.save();
-                            
-                            canvas.drawColor(0xff000000, PorterDuff.Mode.SRC);
-
-                            int yoff;
-                            final boolean scrolling = mScroller != null
-                                    && mScroller.computeScrollOffset();
-                            if (scrolling) {
-                                yoff = mScroller.getCurrY();
-                                mScroller.abortAnimation();
-                            } else {
-                                yoff = mScrollY;
-                            }
-
-                            canvas.translate(0, -yoff);
-                            if (mTranslator != null) {
-                                mTranslator.translateCanvas(canvas);
-                            }
-
-                            mView.draw(canvas);
-
-                            mResizeBufferStartTime = SystemClock.uptimeMillis();
-                            mResizeBufferDuration = mView.getResources().getInteger(
-                                    com.android.internal.R.integer.config_mediumAnimTime);
-                            completed = true;
-
-                            canvas.restoreToCount(restoreCount);
-                        } catch (OutOfMemoryError e) {
-                            Log.w(TAG, "Not enough memory for content change anim buffer", e);
-                        } finally {
-                            if (canvas != null) {
-                                canvas.onPostDraw();
-                            }
-                            if (mResizeBuffer != null) {
-                                mResizeBuffer.end(mAttachInfo.mHardwareCanvas);
-                                if (!completed) {
-                                    mResizeBuffer.destroy();
-                                    mResizeBuffer = null;
-                                }
-                            }
-                        }
-                    }
-                    mAttachInfo.mContentInsets.set(mPendingContentInsets);
-                    host.fitSystemWindows(mAttachInfo.mContentInsets);
+                if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) {
                     insetsChanged = true;
-                    if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
-                            + mAttachInfo.mContentInsets);
                 }
-                if (!mAttachInfo.mVisibleInsets.equals(mPendingVisibleInsets)) {
+                if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
                     mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
                     if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
                             + mAttachInfo.mVisibleInsets);
@@ -1203,6 +1135,71 @@
                 visibleInsetsChanged = !mPendingVisibleInsets.equals(
                         mAttachInfo.mVisibleInsets);
                 if (contentInsetsChanged) {
+                    if (mWidth > 0 && mHeight > 0 &&
+                            mSurface != null && mSurface.isValid() &&
+                            !mAttachInfo.mTurnOffWindowResizeAnim &&
+                            mAttachInfo.mHardwareRenderer != null &&
+                            mAttachInfo.mHardwareRenderer.isEnabled() &&
+                            mAttachInfo.mHardwareRenderer.validate() &&
+                            lp != null && !PixelFormat.formatHasAlpha(lp.format)) {
+
+                        disposeResizeBuffer();
+
+                        boolean completed = false;
+                        HardwareCanvas canvas = null;
+                        try {
+                            if (mResizeBuffer == null) {
+                                mResizeBuffer = mAttachInfo.mHardwareRenderer.createHardwareLayer(
+                                        mWidth, mHeight, false);
+                            } else if (mResizeBuffer.getWidth() != mWidth ||
+                                    mResizeBuffer.getHeight() != mHeight) {
+                                mResizeBuffer.resize(mWidth, mHeight);
+                            }
+                            canvas = mResizeBuffer.start(mAttachInfo.mHardwareCanvas);
+                            canvas.setViewport(mWidth, mHeight);
+                            canvas.onPreDraw(null);
+                            final int restoreCount = canvas.save();
+                            
+                            canvas.drawColor(0xff000000, PorterDuff.Mode.SRC);
+
+                            int yoff;
+                            final boolean scrolling = mScroller != null
+                                    && mScroller.computeScrollOffset();
+                            if (scrolling) {
+                                yoff = mScroller.getCurrY();
+                                mScroller.abortAnimation();
+                            } else {
+                                yoff = mScrollY;
+                            }
+
+                            canvas.translate(0, -yoff);
+                            if (mTranslator != null) {
+                                mTranslator.translateCanvas(canvas);
+                            }
+
+                            mView.draw(canvas);
+
+                            mResizeBufferStartTime = SystemClock.uptimeMillis();
+                            mResizeBufferDuration = mView.getResources().getInteger(
+                                    com.android.internal.R.integer.config_mediumAnimTime);
+                            completed = true;
+
+                            canvas.restoreToCount(restoreCount);
+                        } catch (OutOfMemoryError e) {
+                            Log.w(TAG, "Not enough memory for content change anim buffer", e);
+                        } finally {
+                            if (canvas != null) {
+                                canvas.onPostDraw();
+                            }
+                            if (mResizeBuffer != null) {
+                                mResizeBuffer.end(mAttachInfo.mHardwareCanvas);
+                                if (!completed) {
+                                    mResizeBuffer.destroy();
+                                    mResizeBuffer = null;
+                                }
+                            }
+                        }
+                    }
                     mAttachInfo.mContentInsets.set(mPendingContentInsets);
                     host.fitSystemWindows(mAttachInfo.mContentInsets);
                     if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index f4b9252..c1eec6f 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -441,6 +441,7 @@
      */
     public void scaleCurrentDuration(float scale) {
         mDuration = (long) (mDuration * scale);
+        mStartOffset = (long) (mStartOffset * scale);
     }
 
     /**
diff --git a/core/java/android/view/textservice/SuggestionsInfo.java b/core/java/android/view/textservice/SuggestionsInfo.java
index ed0f89d..62a06b9 100644
--- a/core/java/android/view/textservice/SuggestionsInfo.java
+++ b/core/java/android/view/textservice/SuggestionsInfo.java
@@ -16,6 +16,8 @@
 
 package android.view.textservice;
 
+import com.android.internal.util.ArrayUtils;
+
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -23,7 +25,7 @@
  * This class contains a metadata of suggestions from the text service
  */
 public final class SuggestionsInfo implements Parcelable {
-    private static final String[] EMPTY = new String[0];
+    private static final String[] EMPTY = ArrayUtils.emptyArray(String.class);
 
     /**
      * Flag of the attributes of the suggestions that can be obtained by
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
new file mode 100644
index 0000000..5e3b956
--- /dev/null
+++ b/core/java/android/widget/SpellChecker.java
@@ -0,0 +1,226 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+package android.widget;
+
+import android.content.Context;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.Spanned;
+import android.text.style.SpellCheckSpan;
+import android.text.style.SuggestionSpan;
+import android.util.Log;
+import android.view.textservice.SpellCheckerSession;
+import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
+import android.view.textservice.SuggestionsInfo;
+import android.view.textservice.TextInfo;
+import android.view.textservice.TextServicesManager;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.util.Locale;
+
+
+/**
+ * Helper class for TextView. Bridge between the TextView and the Dictionnary service.
+ *
+ * @hide
+ */
+public class SpellChecker implements SpellCheckerSessionListener {
+    private static final String LOG_TAG = "SpellChecker";
+    private static final boolean DEBUG_SPELL_CHECK = false;
+    private static final int DELAY_BEFORE_SPELL_CHECK = 400; // milliseconds
+
+    private final TextView mTextView;
+
+    final SpellCheckerSession spellCheckerSession;
+    final int mCookie;
+
+    // Paired arrays for the (id, spellCheckSpan) pair. mIndex is the next available position
+    private int[] mIds;
+    private SpellCheckSpan[] mSpellCheckSpans;
+    // The actual current number of used slots in the above arrays
+    private int mLength;
+
+    private int mSpanSequenceCounter = 0;
+    private Runnable mChecker;
+
+    public SpellChecker(TextView textView) {
+        mTextView = textView;
+
+        final TextServicesManager textServicesManager = (TextServicesManager) textView.getContext().
+                getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE);
+        spellCheckerSession = textServicesManager.newSpellCheckerSession(
+                null /* not currently used by the textServicesManager */, Locale.getDefault(),
+                this, true /* means use the languages defined in Settings */);
+        mCookie = hashCode();
+
+        // Arbitrary: 4 simultaneous spell check spans. Will automatically double size on demand
+        final int size = ArrayUtils.idealObjectArraySize(4);
+        mIds = new int[size];
+        mSpellCheckSpans = new SpellCheckSpan[size];
+        mLength = 0;
+    }
+
+    public void addSpellCheckSpan(SpellCheckSpan spellCheckSpan) {
+        int length = mIds.length;
+        if (mLength >= length) {
+            final int newSize = length * 2;
+            int[] newIds = new int[newSize];
+            SpellCheckSpan[] newSpellCheckSpans = new SpellCheckSpan[newSize];
+            System.arraycopy(mIds, 0, newIds, 0, length);
+            System.arraycopy(mSpellCheckSpans, 0, newSpellCheckSpans, 0, length);
+            mIds = newIds;
+            mSpellCheckSpans = newSpellCheckSpans;
+        }
+
+        mIds[mLength] = mSpanSequenceCounter++;
+        mSpellCheckSpans[mLength] = spellCheckSpan;
+        mLength++;
+
+        if (DEBUG_SPELL_CHECK) {
+            final Editable mText = (Editable) mTextView.getText();
+            int start = mText.getSpanStart(spellCheckSpan);
+            int end = mText.getSpanEnd(spellCheckSpan);
+            if (start >= 0 && end >= 0) {
+                Log.d(LOG_TAG, "Schedule check " + mText.subSequence(start, end));
+            } else {
+                Log.d(LOG_TAG, "Schedule check   EMPTY!");
+            }
+        }
+
+        scheduleSpellCheck();
+    }
+
+    public void removeSpellCheckSpan(SpellCheckSpan spellCheckSpan) {
+        for (int i = 0; i < mLength; i++) {
+            if (mSpellCheckSpans[i] == spellCheckSpan) {
+                removeAtIndex(i);
+                return;
+            }
+        }
+    }
+
+    private void removeAtIndex(int i) {
+        System.arraycopy(mIds, i + 1, mIds, i, mLength - i - 1);
+        System.arraycopy(mSpellCheckSpans, i + 1, mSpellCheckSpans, i, mLength - i - 1);
+        mLength--;
+    }
+
+    public void onSelectionChanged() {
+        scheduleSpellCheck();
+    }
+
+    private void scheduleSpellCheck() {
+        if (mLength == 0) return;
+        if (mChecker != null) {
+            mTextView.removeCallbacks(mChecker);
+        }
+        if (mChecker == null) {
+            mChecker = new Runnable() {
+                public void run() {
+                  spellCheck();
+                }
+            };
+        }
+        mTextView.postDelayed(mChecker, DELAY_BEFORE_SPELL_CHECK);
+    }
+
+    private void spellCheck() {
+        final Editable editable = (Editable) mTextView.getText();
+        final int selectionStart = Selection.getSelectionStart(editable);
+        final int selectionEnd = Selection.getSelectionEnd(editable);
+
+        TextInfo[] textInfos = new TextInfo[mLength];
+        int textInfosCount = 0;
+
+        for (int i = 0; i < mLength; i++) {
+            SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i];
+
+            if (spellCheckSpan.isSpellCheckInProgress()) continue;
+
+            final int start = editable.getSpanStart(spellCheckSpan);
+            final int end = editable.getSpanEnd(spellCheckSpan);
+
+            // Do not check this word if the user is currently editing it
+            if (start >= 0 && end >= 0 && (selectionEnd < start || selectionStart > end)) {
+                final String word = editable.subSequence(start, end).toString();
+                spellCheckSpan.setSpellCheckInProgress();
+                textInfos[textInfosCount++] = new TextInfo(word, mCookie, mIds[i]);
+            }
+        }
+
+        if (textInfosCount > 0) {
+            if (textInfosCount < mLength) {
+                TextInfo[] textInfosCopy = new TextInfo[textInfosCount];
+                System.arraycopy(textInfos, 0, textInfosCopy, 0, textInfosCount);
+                textInfos = textInfosCopy;
+            }
+            spellCheckerSession.getSuggestions(textInfos, SuggestionSpan.SUGGESTIONS_MAX_SIZE,
+                    false /* TODO Set sequentialWords to true for initial spell check */);
+        }
+    }
+
+    @Override
+    public void onGetSuggestions(SuggestionsInfo[] results) {
+        final Editable editable = (Editable) mTextView.getText();
+        for (int i = 0; i < results.length; i++) {
+            SuggestionsInfo suggestionsInfo = results[i];
+            if (suggestionsInfo.getCookie() != mCookie) continue;
+
+            final int sequenceNumber = suggestionsInfo.getSequence();
+            // Starting from the end, to limit the number of array copy while removing
+            for (int j = mLength - 1; j >= 0; j--) {
+                if (sequenceNumber == mIds[j]) {
+                    SpellCheckSpan spellCheckSpan = mSpellCheckSpans[j];
+                    final int attributes = suggestionsInfo.getSuggestionsAttributes();
+                    boolean isInDictionary =
+                            ((attributes & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) > 0);
+                    boolean looksLikeTypo =
+                            ((attributes & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) > 0);
+
+                    if (DEBUG_SPELL_CHECK) {
+                        final int start = editable.getSpanStart(spellCheckSpan);
+                        final int end = editable.getSpanEnd(spellCheckSpan);
+                        Log.d(LOG_TAG, "Result sequence=" + suggestionsInfo.getSequence() + " " +
+                                editable.subSequence(start, end) +
+                                "\t" + (isInDictionary?"IN_DICT" : "NOT_DICT") +
+                                "\t" + (looksLikeTypo?"TYPO" : "NOT_TYPO"));
+                    }
+
+                    if (!isInDictionary && looksLikeTypo) {
+                        String[] suggestions = getSuggestions(suggestionsInfo);
+                        if (suggestions.length > 0) {
+                            SuggestionSpan suggestionSpan = new SuggestionSpan(
+                                    mTextView.getContext(), suggestions,
+                                    SuggestionSpan.FLAG_EASY_CORRECT |
+                                    SuggestionSpan.FLAG_MISSPELLED);
+                            final int start = editable.getSpanStart(spellCheckSpan);
+                            final int end = editable.getSpanEnd(spellCheckSpan);
+                            editable.setSpan(suggestionSpan, start, end,
+                                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                            // TODO limit to the word rectangle region
+                            mTextView.invalidate();
+
+                            if (DEBUG_SPELL_CHECK) {
+                                String suggestionsString = "";
+                                for (String s : suggestions) { suggestionsString += s + "|"; }
+                                Log.d(LOG_TAG, "  Suggestions for " + sequenceNumber + " " +
+                                    editable.subSequence(start, end)+ "  " + suggestionsString);
+                            }
+                        }
+                    }
+                    editable.removeSpan(spellCheckSpan);
+                }
+            }
+        }
+    }
+
+    private static String[] getSuggestions(SuggestionsInfo suggestionsInfo) {
+        final int len = Math.max(0, suggestionsInfo.getSuggestionsCount());
+        String[] suggestions = new String[len];
+        for (int j = 0; j < len; ++j) {
+            suggestions[j] = suggestionsInfo.getSuggestionAt(j);
+        }
+        return suggestions;
+    }
+}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 662b964..683a984 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -61,11 +61,6 @@
 import android.text.StaticLayout;
 import android.text.TextDirectionHeuristic;
 import android.text.TextDirectionHeuristics;
-import android.text.TextDirectionHeuristics.AnyStrong;
-import android.text.TextDirectionHeuristics.CharCount;
-import android.text.TextDirectionHeuristics.FirstStrong;
-import android.text.TextDirectionHeuristics.TextDirectionAlgorithm;
-import android.text.TextDirectionHeuristics.TextDirectionHeuristicImpl;
 import android.text.TextPaint;
 import android.text.TextUtils;
 import android.text.TextWatcher;
@@ -88,6 +83,7 @@
 import android.text.method.WordIterator;
 import android.text.style.ClickableSpan;
 import android.text.style.ParagraphStyle;
+import android.text.style.SpellCheckSpan;
 import android.text.style.SuggestionSpan;
 import android.text.style.TextAppearanceSpan;
 import android.text.style.URLSpan;
@@ -220,7 +216,6 @@
  * @attr ref android.R.styleable#TextView_imeActionLabel
  * @attr ref android.R.styleable#TextView_imeActionId
  * @attr ref android.R.styleable#TextView_editorExtras
- * @attr ref android.R.styleable#TextView_suggestionsEnabled
  */
 @RemoteView
 public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
@@ -334,7 +329,6 @@
     private int mTextEditSuggestionItemLayout;
     private SuggestionsPopupWindow mSuggestionsPopupWindow;
     private SuggestionRangeSpan mSuggestionRangeSpan;
-    private boolean mSuggestionsEnabled = true;
 
     private int mCursorDrawableRes;
     private final Drawable[] mCursorDrawable = new Drawable[2];
@@ -356,6 +350,8 @@
 
     private WordIterator mWordIterator;
 
+    private SpellChecker mSpellChecker;
+
     // The alignment to pass to Layout, or null if not resolved.
     private Layout.Alignment mLayoutAlignment;
 
@@ -826,10 +822,6 @@
                 mTextIsSelectable = a.getBoolean(attr, false);
                 break;
 
-            case com.android.internal.R.styleable.TextView_suggestionsEnabled:
-                mSuggestionsEnabled = a.getBoolean(attr, true);
-                break;
-
             case com.android.internal.R.styleable.TextView_textAllCaps:
                 allCaps = a.getBoolean(attr, false);
                 break;
@@ -3100,18 +3092,19 @@
         }
 
         boolean needEditableForNotification = false;
+        boolean startSpellCheck = false;
 
         if (mListeners != null && mListeners.size() != 0) {
             needEditableForNotification = true;
         }
 
-        if (type == BufferType.EDITABLE || mInput != null ||
-            needEditableForNotification) {
+        if (type == BufferType.EDITABLE || mInput != null || needEditableForNotification) {
             Editable t = mEditableFactory.newEditable(text);
             text = t;
             setFilters(t, mFilters);
             InputMethodManager imm = InputMethodManager.peekInstance();
             if (imm != null) imm.restartInput(this);
+            startSpellCheck = true;
         } else if (type == BufferType.SPANNABLE || mMovement != null) {
             text = mSpannableFactory.newSpannable(text);
         } else if (!(text instanceof CharWrapper)) {
@@ -3200,6 +3193,10 @@
         sendOnTextChanged(text, 0, oldlen, textLength);
         onTextChanged(text, 0, oldlen, textLength);
 
+        if (startSpellCheck) {
+            updateSpellCheckSpans(0, textLength);
+        }
+
         if (needEditableForNotification) {
             sendAfterTextChanged((Editable) text);
         }
@@ -7113,8 +7110,9 @@
      * to turn off ellipsizing.
      *
      * If {@link #setMaxLines} has been used to set two or more lines,
-     * {@link TextUtils.TruncateAt#END} and {@link TextUtils.TruncateAt#MARQUEE}
-     * are only supported (other ellipsizing types will not do anything).
+     * {@link android.text.TextUtils.TruncateAt#END} and
+     * {@link android.text.TextUtils.TruncateAt#MARQUEE}* are only supported
+     * (other ellipsizing types will not do anything).
      *
      * @attr ref android.R.styleable#TextView_ellipsize
      */
@@ -7376,7 +7374,7 @@
      * @param lengthAfter The length of the replacement modified text
      */
     protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
-        // intentionally empty
+        // intentionally empty, template pattern method can be overridden by subclasses
     }
 
     /**
@@ -7388,6 +7386,9 @@
      */
     protected void onSelectionChanged(int selStart, int selEnd) {
         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
+        if (mSpellChecker != null) {
+            mSpellChecker.onSelectionChanged();
+        }
     }
 
     /**
@@ -7422,8 +7423,7 @@
         }
     }
 
-    private void sendBeforeTextChanged(CharSequence text, int start, int before,
-                                   int after) {
+    private void sendBeforeTextChanged(CharSequence text, int start, int before, int after) {
         if (mListeners != null) {
             final ArrayList<TextWatcher> list = mListeners;
             final int count = list.size();
@@ -7431,14 +7431,32 @@
                 list.get(i).beforeTextChanged(text, start, before, after);
             }
         }
+
+        // The spans that are inside or intersect the modified region no longer make sense
+        removeIntersectingSpans(start, start + before, SpellCheckSpan.class);
+        removeIntersectingSpans(start, start + before, SuggestionSpan.class);
+    }
+
+    // Removes all spans that are inside or actually overlap the start..end range
+    private <T> void removeIntersectingSpans(int start, int end, Class<T> type) {
+        if (!(mText instanceof Editable)) return;
+        Editable text = (Editable) mText;
+
+        T[] spans = text.getSpans(start, end, type);
+        final int length = spans.length;
+        for (int i = 0; i < length; i++) {
+            final int s = text.getSpanStart(spans[i]);
+            final int e = text.getSpanEnd(spans[i]);
+            if (e == start || s == end) break;
+            text.removeSpan(spans[i]);
+        }
     }
 
     /**
      * Not private so it can be called from an inner class without going
      * through a thunk.
      */
-    void sendOnTextChanged(CharSequence text, int start, int before,
-                                   int after) {
+    void sendOnTextChanged(CharSequence text, int start, int before, int after) {
         if (mListeners != null) {
             final ArrayList<TextWatcher> list = mListeners;
             final int count = list.size();
@@ -7486,6 +7504,11 @@
         sendOnTextChanged(buffer, start, before, after);
         onTextChanged(buffer, start, before, after);
 
+        // The WordIterator text change listener may be called after this one.
+        // Make sure this changed text is rescanned before the iterator is used on it.
+        getWordIterator().forceUpdate();
+        updateSpellCheckSpans(start, start + after);
+
         // Hide the controllers if the amount of content changed
         if (before != after) {
             hideControllers();
@@ -7573,7 +7596,7 @@
                 }
             }
         }
-        
+
         if (what instanceof ParcelableSpan) {
             // If this is a span that can be sent to a remote process,
             // the current extract editor would be interested in it.
@@ -7603,10 +7626,102 @@
                 }
             }
         }
+
+        if (what instanceof SpellCheckSpan) {
+            if (newStart < 0) {
+                getSpellChecker().removeSpellCheckSpan((SpellCheckSpan) what);
+            } else if (oldStart < 0) {
+                getSpellChecker().addSpellCheckSpan((SpellCheckSpan) what);
+            }
+        }
+
+        if (what instanceof SuggestionSpan) {
+            if (newStart < 0) {
+                Log.d("spellcheck", "REMOVE suggspan " + mText.subSequence(oldStart, oldEnd));
+            }
+        }
     }
 
-    private class ChangeWatcher
-    implements TextWatcher, SpanWatcher {
+    /**
+     * Create new SpellCheckSpans on the modified region.
+     */
+    private void updateSpellCheckSpans(int start, int end) {
+        if (!(mText instanceof Editable) || !isSuggestionsEnabled()) return;
+        Editable text = (Editable) mText;
+
+        WordIterator wordIterator = getWordIterator();
+        wordIterator.setCharSequence(text);
+
+        // Move back to the beginning of the current word, if any
+        int wordStart = wordIterator.preceding(start);
+        int wordEnd;
+        if (wordStart == BreakIterator.DONE) {
+            wordEnd = wordIterator.following(start);
+            if (wordEnd != BreakIterator.DONE) {
+                wordStart = wordIterator.getBeginning(wordEnd);
+            }
+        } else {
+            wordEnd = wordIterator.getEnd(wordStart);
+        }
+        if (wordEnd == BreakIterator.DONE) {
+            return;
+        }
+
+        // Iterate over the newly added text and schedule new SpellCheckSpans
+        while (wordStart <= end) {
+            if (wordEnd >= start) {
+                // A word across the interval boundaries must remove boundary edition spans
+                if (wordStart < start && wordEnd > start) {
+                    removeEditionSpansAt(start, text);
+                }
+
+                if (wordStart < end && wordEnd > end) {
+                    removeEditionSpansAt(end, text);
+                }
+
+                // Do not create new boundary spans if they already exist
+                boolean createSpellCheckSpan = true;
+                if (wordEnd == start) {
+                    SpellCheckSpan[] spellCheckSpans = text.getSpans(start, start,
+                            SpellCheckSpan.class);
+                    if (spellCheckSpans.length > 0) createSpellCheckSpan = false;
+                }
+
+                if (wordStart == end) {
+                    SpellCheckSpan[] spellCheckSpans = text.getSpans(end, end,
+                            SpellCheckSpan.class);
+                    if (spellCheckSpans.length > 0) createSpellCheckSpan = false;
+                }
+
+                if (createSpellCheckSpan) {
+                    text.setSpan(new SpellCheckSpan(), wordStart, wordEnd,
+                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                }
+            }
+
+            // iterate word by word
+            wordEnd = wordIterator.following(wordEnd);
+            if (wordEnd == BreakIterator.DONE) return;
+            wordStart = wordIterator.getBeginning(wordEnd);
+            if (wordStart == BreakIterator.DONE) {
+                Log.e(LOG_TAG, "Unable to find word beginning from " + wordEnd + "in " + mText);
+                return;
+            }
+        }
+    }
+
+    private static void removeEditionSpansAt(int offset, Editable text) {
+        SuggestionSpan[] suggestionSpans = text.getSpans(offset, offset, SuggestionSpan.class);
+        for (int i = 0; i < suggestionSpans.length; i++) {
+            text.removeSpan(suggestionSpans[i]);
+        }
+        SpellCheckSpan[] spellCheckSpans = text.getSpans(offset, offset, SpellCheckSpan.class);
+        for (int i = 0; i < spellCheckSpans.length; i++) {
+            text.removeSpan(spellCheckSpans[i]);
+        }
+    }
+
+    private class ChangeWatcher implements TextWatcher, SpanWatcher {
 
         private CharSequence mBeforeText;
 
@@ -7631,8 +7746,7 @@
             TextView.this.handleTextChanged(buffer, start, before, after);
 
             if (AccessibilityManager.getInstance(mContext).isEnabled() &&
-                    (isFocused() || isSelected() &&
-                    isShown())) {
+                    (isFocused() || isSelected() && isShown())) {
                 sendAccessibilityEventTypeViewTextChanged(mBeforeText, start, before, after);
                 mBeforeText = null;
             }
@@ -7642,8 +7756,7 @@
             if (DEBUG_EXTRACT) Log.v(LOG_TAG, "afterTextChanged: " + buffer);
             TextView.this.sendAfterTextChanged(buffer);
 
-            if (MetaKeyKeyListener.getMetaState(buffer,
-                                 MetaKeyKeyListener.META_SELECTING) != 0) {
+            if (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0) {
                 MetaKeyKeyListener.stopSelecting(TextView.this, buffer);
             }
         }
@@ -7841,17 +7954,20 @@
             if (mInputContentType != null) {
                 mInputContentType.enterDown = false;
             }
+
             hideControllers();
-            removeAllSuggestionSpans();
+
+            removeSpans(0, mText.length(), SuggestionSpan.class);
+            removeSpans(0, mText.length(), SpellCheckSpan.class);
         }
 
         startStopMarquee(hasWindowFocus);
     }
 
-    private void removeAllSuggestionSpans() {
+    private void removeSpans(int start, int end, Class<?> type) {
         if (mText instanceof Editable) {
             Editable editable = ((Editable) mText);
-            SuggestionSpan[] spans = editable.getSpans(0, mText.length(), SuggestionSpan.class);
+            Object[] spans = editable.getSpans(start, end, type);
             final int length = spans.length;
             for (int i = 0; i < length; i++) {
                 editable.removeSpan(spans[i]);
@@ -7969,6 +8085,8 @@
                         }
                     }
                 }
+
+                handled = true;
             }
 
             if (handled) {
@@ -7980,11 +8098,22 @@
     }
 
     /**
+     * @return <code>true</code> if the cursor/current selection overlaps a {@link SuggestionSpan}.
+     */
+    private boolean isCursorInsideSuggestionSpan() {
+        if (!(mText instanceof Spannable)) return false;
+
+        SuggestionSpan[] suggestionSpans = ((Spannable) mText).getSpans(getSelectionStart(),
+                getSelectionEnd(), SuggestionSpan.class);
+        return (suggestionSpans.length > 0);
+    }
+
+    /**
      * @return <code>true</code> if the cursor is inside an {@link SuggestionSpan} with
      * {@link SuggestionSpan#FLAG_EASY_CORRECT} set.
      */
     private boolean isCursorInsideEasyCorrectionSpan() {
-        Spannable spannable = (Spannable) TextView.this.mText;
+        Spannable spannable = (Spannable) mText;
         SuggestionSpan[] suggestionSpans = spannable.getSpans(getSelectionStart(),
                 getSelectionEnd(), SuggestionSpan.class);
         for (int i = 0; i < suggestionSpans.length; i++) {
@@ -8445,16 +8574,14 @@
             selectionStart = ((Spanned) mText).getSpanStart(url);
             selectionEnd = ((Spanned) mText).getSpanEnd(url);
         } else {
-            if (mWordIterator == null) {
-                mWordIterator = new WordIterator();
-            }
-            // WordIerator handles text changes, this is a no-op if text in unchanged.
-            mWordIterator.setCharSequence(mText);
+            WordIterator wordIterator = getWordIterator();
+            // WordIterator handles text changes, this is a no-op if text in unchanged.
+            wordIterator.setCharSequence(mText);
 
-            selectionStart = mWordIterator.getBeginning(minOffset);
+            selectionStart = wordIterator.getBeginning(minOffset);
             if (selectionStart == BreakIterator.DONE) return false;
 
-            selectionEnd = mWordIterator.getEnd(maxOffset);
+            selectionEnd = wordIterator.getEnd(maxOffset);
             if (selectionEnd == BreakIterator.DONE) return false;
         }
 
@@ -8462,6 +8589,20 @@
         return true;
     }
 
+    WordIterator getWordIterator() {
+        if (mWordIterator == null) {
+            mWordIterator = new WordIterator();
+        }
+        return mWordIterator;
+    }
+
+    private SpellChecker getSpellChecker() {
+        if (mSpellChecker == null) {
+            mSpellChecker = new SpellChecker(this);
+        }
+        return mSpellChecker;
+    }
+
     private long getLastTouchOffsets() {
         int minOffset, maxOffset;
 
@@ -8790,7 +8931,7 @@
             final int offset = getOffsetForPosition(mLastDownPositionX, mLastDownPositionY);
             stopSelectionActionMode();
             Selection.setSelection((Spannable) mText, offset);
-            getInsertionController().showImmediately();
+            getInsertionController().showWithActionPopup();
             handled = true;
         }
 
@@ -9067,10 +9208,12 @@
     }
 
     private class SuggestionsPopupWindow extends PinnedPopupWindow implements OnClickListener {
-        private static final int MAX_NUMBER_SUGGESTIONS = 5;
+        private static final int MAX_NUMBER_SUGGESTIONS = SuggestionSpan.SUGGESTIONS_MAX_SIZE;
         private static final int NO_SUGGESTIONS = -1;
+        private static final float AVERAGE_HIGHLIGHTS_PER_SUGGESTION = 1.4f;
         private WordIterator mSuggestionWordIterator;
-        private TextAppearanceSpan[] mHighlightSpans = new TextAppearanceSpan[0];
+        private TextAppearanceSpan[] mHighlightSpans = new TextAppearanceSpan
+                [(int) (AVERAGE_HIGHLIGHTS_PER_SUGGESTION * MAX_NUMBER_SUGGESTIONS)];
 
         @Override
         protected void createPopupWindow() {
@@ -9149,9 +9292,10 @@
         @Override
         public void show() {
             if (!(mText instanceof Editable)) return;
-            updateSuggestions();
 
-            super.show();
+            if (updateSuggestions()) {
+                super.show();
+            }
         }
 
         @Override
@@ -9179,7 +9323,7 @@
             }
         }
 
-        private void updateSuggestions() {
+        private boolean updateSuggestions() {
             Spannable spannable = (Spannable)TextView.this.mText;
             SuggestionSpan[] suggestionSpans = getSuggestionSpans();
 
@@ -9217,22 +9361,15 @@
                 }
             }
 
-            if (totalNbSuggestions == 0) {
-                // TODO Replace by final text, use a dedicated layout, add a fade out timer...
-                TextView textView = (TextView) mContentView.getChildAt(0);
-                textView.setText("No suggestions available");
-                SuggestionInfo suggestionInfo = (SuggestionInfo) textView.getTag();
-                suggestionInfo.spanStart = NO_SUGGESTIONS;
-                totalNbSuggestions++;
-            } else {
-                if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = new SuggestionRangeSpan();
-                ((Editable) mText).setSpan(mSuggestionRangeSpan, spanUnionStart, spanUnionEnd,
-                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+            if (totalNbSuggestions == 0) return false;
 
-                for (int i = 0; i < totalNbSuggestions; i++) {
-                    final TextView textView = (TextView) mContentView.getChildAt(i);
-                    highlightTextDifferences(textView, spanUnionStart, spanUnionEnd);
-                }
+            if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = new SuggestionRangeSpan();
+            ((Editable) mText).setSpan(mSuggestionRangeSpan, spanUnionStart, spanUnionEnd,
+                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+            for (int i = 0; i < totalNbSuggestions; i++) {
+                final TextView textView = (TextView) mContentView.getChildAt(i);
+                highlightTextDifferences(textView, spanUnionStart, spanUnionEnd);
             }
 
             for (int i = 0; i < totalNbSuggestions; i++) {
@@ -9241,6 +9378,27 @@
             for (int i = totalNbSuggestions; i < MAX_NUMBER_SUGGESTIONS; i++) {
                 mContentView.getChildAt(i).setVisibility(GONE);
             }
+
+            return true;
+        }
+
+        private void onDictionarySuggestionsReceived(String[] suggestions) {
+            if (suggestions.length == 0) {
+                // TODO Actual implementation of this feature
+                suggestions = new String[] {"Add to dictionary"};
+            }
+
+            WordIterator wordIterator = getWordIterator();
+            wordIterator.setCharSequence(mText);
+
+            final int pos = getSelectionStart();
+            int wordStart = wordIterator.getBeginning(pos);
+            int wordEnd = wordIterator.getEnd(pos);
+
+            SuggestionSpan suggestionSpan = new SuggestionSpan(getContext(), suggestions, 0);
+            ((Editable) mText).setSpan(suggestionSpan, wordStart, wordEnd,
+                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+            show();
         }
 
         private long[] getWordLimits(CharSequence text) {
@@ -9422,6 +9580,15 @@
                     final String originalText = mText.subSequence(spanStart, spanEnd).toString();
                     ((Editable) mText).replace(spanStart, spanEnd, suggestion);
 
+                    // A replacement on a misspelled text removes the misspelled flag.
+                    // TODO restore the flag if the misspelled word is selected back?
+                    int suggestionSpanFlags = suggestionInfo.suggestionSpan.getFlags();
+                    if ((suggestionSpanFlags & SuggestionSpan.FLAG_MISSPELLED) > 0) {
+                        suggestionSpanFlags &= ~(SuggestionSpan.FLAG_MISSPELLED);
+                        suggestionSpanFlags &= ~(SuggestionSpan.FLAG_EASY_CORRECT);
+                        suggestionInfo.suggestionSpan.setFlags(suggestionSpanFlags);
+                    }
+
                     // Notify source IME of the suggestion pick. Do this before swaping texts.
                     if (!TextUtils.isEmpty(
                             suggestionInfo.suggestionSpan.getNotificationTargetClassName())) {
@@ -9471,53 +9638,46 @@
 
     boolean areSuggestionsShown() {
         return mSuggestionsPopupWindow != null && mSuggestionsPopupWindow.isShowing();
-    } 
+    }
+
+    void onDictionarySuggestionsReceived(String[] suggestions) {
+        if (mSuggestionsPopupWindow != null) {
+            mSuggestionsPopupWindow.onDictionarySuggestionsReceived(suggestions);
+        }
+    }
 
     /**
-     * Some parts of the text can have alternate suggestion text attached. This is typically done by
-     * the IME by adding {@link SuggestionSpan}s to the text.
+     * Return whether or not suggestions are enabled on this TextView. The suggestions are generated
+     * by the IME or by the spell checker as the user types. This is done by adding
+     * {@link SuggestionSpan}s to the text.
      *
      * When suggestions are enabled (default), this list of suggestions will be displayed when the
-     * user double taps on these parts of the text. No suggestions are displayed when this value is
-     * false. Use {@link #setSuggestionsEnabled(boolean)} to change this value.
+     * user asks for them on these parts of the text. This value depends on the inputType of this
+     * TextView.
      *
-     * Note that suggestions are only enabled for a subset of input types. In addition to setting
-     * this flag to <code>true</code> using {@link #setSuggestionsEnabled(boolean)} or the
-     * <code>android:suggestionsEnabled</code> xml attribute, this method will return
-     * <code>true</code> only if the class of your input type is {@link InputType#TYPE_CLASS_TEXT}.
-     * In addition, the type variation must also be one of
+     * The class of the input type must be {@link InputType#TYPE_CLASS_TEXT}.
+     *
+     * In addition, the type variation must be one of
      * {@link InputType#TYPE_TEXT_VARIATION_NORMAL},
      * {@link InputType#TYPE_TEXT_VARIATION_EMAIL_SUBJECT},
      * {@link InputType#TYPE_TEXT_VARIATION_LONG_MESSAGE},
      * {@link InputType#TYPE_TEXT_VARIATION_SHORT_MESSAGE} or
      * {@link InputType#TYPE_TEXT_VARIATION_WEB_EDIT_TEXT}.
      *
-     * @return true if the suggestions popup window is enabled.
+     * And finally, the {@link InputType#TYPE_TEXT_FLAG_NO_SUGGESTIONS} flag must <i>not</i> be set.
      *
-     * @attr ref android.R.styleable#TextView_suggestionsEnabled
+     * @return true if the suggestions popup window is enabled, based on the inputType.
      */
     public boolean isSuggestionsEnabled() {
-        if (!mSuggestionsEnabled) return false;
         if ((mInputType & InputType.TYPE_MASK_CLASS) != InputType.TYPE_CLASS_TEXT) return false;
+        if ((mInputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) > 0) return false;
+
         final int variation = mInputType & EditorInfo.TYPE_MASK_VARIATION;
-        if (variation == EditorInfo.TYPE_TEXT_VARIATION_NORMAL ||
+        return (variation == EditorInfo.TYPE_TEXT_VARIATION_NORMAL ||
                 variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_SUBJECT ||
                 variation == EditorInfo.TYPE_TEXT_VARIATION_LONG_MESSAGE ||
                 variation == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE ||
-                variation == EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) return true;
-
-        return false;
-    }
-
-    /**
-     * Enables or disables the suggestion popup. See {@link #isSuggestionsEnabled()}.
-     *
-     * @param enabled Whether or not suggestions are enabled.
-     *
-     * @attr ref android.R.styleable#TextView_suggestionsEnabled
-     */
-    public void setSuggestionsEnabled(boolean enabled) {
-        mSuggestionsEnabled = enabled;
+                variation == EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT);
     }
 
     /**
@@ -9787,11 +9947,11 @@
         @Override
         public void show() {
             boolean canPaste = canPaste();
-            boolean suggestionsEnabled = isSuggestionsEnabled();
+            boolean canSuggest = isSuggestionsEnabled() && isCursorInsideSuggestionSpan();
             mPasteTextView.setVisibility(canPaste ? View.VISIBLE : View.GONE);
-            mReplaceTextView.setVisibility(suggestionsEnabled ? View.VISIBLE : View.GONE);
+            mReplaceTextView.setVisibility(canSuggest ? View.VISIBLE : View.GONE);
 
-            if (!canPaste && !suggestionsEnabled) return;
+            if (!canPaste && !canSuggest) return;
 
             super.show();
         }
@@ -9802,6 +9962,9 @@
                 onTextContextMenuItem(ID_PASTE);
                 hide();
             } else if (view == mReplaceTextView) {
+                final int middle = (getSelectionStart() + getSelectionEnd()) / 2;
+                stopSelectionActionMode();
+                Selection.setSelection((Spannable) mText, middle);
                 showSuggestions();
             }
         }
@@ -10133,17 +10296,18 @@
         @Override
         public void show() {
             super.show();
-            hideAfterDelay();
-        }
-
-        public void show(int delayBeforeShowActionPopup) {
-            show();
 
             final long durationSinceCutOrCopy = SystemClock.uptimeMillis() - sLastCutOrCopyTime;
             if (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION) {
-                delayBeforeShowActionPopup = 0;
+                showActionPopupWindow(0);
             }
-            showActionPopupWindow(delayBeforeShowActionPopup);
+
+            hideAfterDelay();
+        }
+
+        public void showWithActionPopup() {
+            show();
+            showActionPopupWindow(0);
         }
 
         private void hideAfterDelay() {
@@ -10194,7 +10358,7 @@
                                 // Tapping on the handle dismisses the displayed action popup
                                 mActionPopupWindow.hide();
                             } else {
-                                show(0);
+                                showWithActionPopup();
                             }
                         }
                     }
@@ -10349,16 +10513,14 @@
     }
 
     private class InsertionPointCursorController implements CursorController {
-        private static final int DELAY_BEFORE_PASTE_ACTION = 1600;
-
         private InsertionHandleView mHandle;
 
         public void show() {
-            getHandle().show(DELAY_BEFORE_PASTE_ACTION);
+            getHandle().show();
         }
 
-        public void showImmediately() {
-            getHandle().show(0);
+        public void showWithActionPopup() {
+            getHandle().showWithActionPopup();
         }
 
         public void hide() {
@@ -10390,7 +10552,7 @@
     }
 
     private class SelectionModifierCursorController implements CursorController {
-        private static final int DELAY_BEFORE_REPLACE_ACTION = 1200;
+        private static final int DELAY_BEFORE_REPLACE_ACTION = 200; // milliseconds
         // The cursor controller handles, lazily created when shown.
         private SelectionStartHandleView mStartHandle;
         private SelectionEndHandleView mEndHandle;
@@ -10879,8 +11041,8 @@
     private int                     mAutoLinkMask;
     private boolean                 mLinksClickable = true;
 
-    private float                   mSpacingMult = 1;
-    private float                   mSpacingAdd = 0;
+    private float                   mSpacingMult = 1.0f;
+    private float                   mSpacingAdd = 0.0f;
     private boolean                 mTextIsSelectable = false;
 
     private static final int        LINES = 1;
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index d613921..b355c41 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -31,6 +31,7 @@
     private static final String TAG = "ActionMenuView";
     
     static final int MIN_CELL_SIZE = 56; // dips
+    static final int GENERATED_ITEM_PADDING = 4; // dips
 
     private MenuBuilder mMenu;
 
@@ -39,6 +40,7 @@
     private boolean mFormatItems;
     private int mFormatItemsWidth;
     private int mMinCellSize;
+    private int mGeneratedItemPadding;
     private int mMeasuredExtraWidth;
 
     public ActionMenuView(Context context) {
@@ -48,7 +50,9 @@
     public ActionMenuView(Context context, AttributeSet attrs) {
         super(context, attrs);
         setBaselineAligned(false);
-        mMinCellSize = (int) (MIN_CELL_SIZE * context.getResources().getDisplayMetrics().density);
+        final float density = context.getResources().getDisplayMetrics().density;
+        mMinCellSize = (int) (MIN_CELL_SIZE * density);
+        mGeneratedItemPadding = (int) (GENERATED_ITEM_PADDING * density);
     }
 
     public void setPresenter(ActionMenuPresenter presenter) {
@@ -133,8 +137,15 @@
             final View child = getChildAt(i);
             if (child.getVisibility() == GONE) continue;
 
+            final boolean isGeneratedItem = child instanceof ActionMenuItemView;
             visibleItemCount++;
 
+            if (isGeneratedItem) {
+                // Reset padding for generated menu item views; it may change below
+                // and views are recycled.
+                child.setPadding(mGeneratedItemPadding, 0, mGeneratedItemPadding, 0);
+            }
+
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
             lp.expanded = false;
             lp.extraPixels = 0;
@@ -142,6 +153,7 @@
             lp.expandable = false;
             lp.leftMargin = 0;
             lp.rightMargin = 0;
+            lp.preventEdgeOffset = isGeneratedItem && ((ActionMenuItemView) child).hasText();
 
             // Overflow always gets 1 cell. No more, no less.
             final int cellsAvailable = lp.isOverflowButton ? 1 : cellsRemaining;
@@ -158,6 +170,10 @@
             if (cellsUsed == 1) smallestItemsAt |= (1 << i);
         }
 
+        // When we have overflow and a single expanded (text) item, we want to try centering it
+        // visually in the available space even though overflow consumes some of it.
+        final boolean centerSingleExpandedItem = hasOverflow && visibleItemCount == 2;
+
         // Divide space for remaining cells if we have items that can expand.
         // Try distributing whole leftover cells to smaller items first.
 
@@ -184,16 +200,27 @@
                 }
             }
 
-            if (minCellsItemCount > cellsRemaining) break; // Couldn't expand anything evenly. Stop.
-
             // Items that get expanded will always be in the set of smallest items when we're done.
             smallestItemsAt |= minCellsAt;
 
-            for (int i = 0; i < childCount; i++) {
-                if ((minCellsAt & (1 << i)) == 0) continue;
+            if (minCellsItemCount > cellsRemaining) break; // Couldn't expand anything evenly. Stop.
 
+            // We have enough cells, all minimum size items will be incremented.
+            minCells++;
+
+            for (int i = 0; i < childCount; i++) {
                 final View child = getChildAt(i);
                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+                if ((minCellsAt & (1 << i)) == 0) {
+                    // If this item is already at our small item count, mark it for later.
+                    if (lp.cellsUsed == minCells) smallestItemsAt |= 1 << i;
+                    continue;
+                }
+
+                if (centerSingleExpandedItem && lp.preventEdgeOffset && cellsRemaining == 1) {
+                    // Add padding to this item such that it centers.
+                    child.setPadding(mGeneratedItemPadding + cellSize, 0, mGeneratedItemPadding, 0);
+                }
                 lp.cellsUsed++;
                 lp.expanded = true;
                 cellsRemaining--;
@@ -207,16 +234,18 @@
 
         final boolean singleItem = !hasOverflow && visibleItemCount == 1;
         if (cellsRemaining > 0 && smallestItemsAt != 0 &&
-                (cellsRemaining < visibleItemCount - 1 || singleItem)) {
+                (cellsRemaining < visibleItemCount - 1 || singleItem || maxCellsUsed > 1)) {
             float expandCount = Long.bitCount(smallestItemsAt);
 
             if (!singleItem) {
                 // The items at the far edges may only expand by half in order to pin to either side.
                 if ((smallestItemsAt & 1) != 0) {
-                    expandCount -= 0.5f;
+                    LayoutParams lp = (LayoutParams) getChildAt(0).getLayoutParams();
+                    if (!lp.preventEdgeOffset) expandCount -= 0.5f;
                 }
                 if ((smallestItemsAt & (1 << (childCount - 1))) != 0) {
-                    expandCount -= 0.5f;
+                    LayoutParams lp = ((LayoutParams) getChildAt(childCount - 1).getLayoutParams());
+                    if (!lp.preventEdgeOffset) expandCount -= 0.5f;
                 }
             }
 
@@ -232,7 +261,7 @@
                     // If this is one of our views, expand and measure at the larger size.
                     lp.extraPixels = extraPixels;
                     lp.expanded = true;
-                    if (i == 0) {
+                    if (i == 0 && !lp.preventEdgeOffset) {
                         // First item gets part of its new padding pushed out of sight.
                         // The last item will get this implicitly from layout.
                         lp.leftMargin = -extraPixels / 2;
@@ -496,6 +525,8 @@
         public int extraPixels;
         @ViewDebug.ExportedProperty(category = "layout")
         public boolean expandable;
+        @ViewDebug.ExportedProperty(category = "layout")
+        public boolean preventEdgeOffset;
 
         public boolean expanded;
 
diff --git a/core/res/res/drawable-hdpi/ic_menu_selectall_holo_dark.png b/core/res/res/drawable-hdpi/ic_menu_selectall_holo_dark.png
index 5579443..b161361 100644
--- a/core/res/res/drawable-hdpi/ic_menu_selectall_holo_dark.png
+++ b/core/res/res/drawable-hdpi/ic_menu_selectall_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_selectall_holo_light.png b/core/res/res/drawable-hdpi/ic_menu_selectall_holo_light.png
index 6674914..0a7b364 100644
--- a/core/res/res/drawable-hdpi/ic_menu_selectall_holo_light.png
+++ b/core/res/res/drawable-hdpi/ic_menu_selectall_holo_light.png
Binary files differ
diff --git a/core/res/res/layout/keyguard_screen_password_landscape.xml b/core/res/res/layout/keyguard_screen_password_landscape.xml
index bc86ab7..12df99e 100644
--- a/core/res/res/layout/keyguard_screen_password_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_password_landscape.xml
@@ -151,7 +151,6 @@
             android:background="@drawable/lockscreen_password_field_dark"
             android:textColor="?android:attr/textColorPrimary"
             android:imeOptions="flagNoFullscreen|actionDone"
-            android:suggestionsEnabled="false"
             />
 
         <ImageView android:id="@+id/switch_ime_button"
diff --git a/core/res/res/layout/keyguard_screen_password_portrait.xml b/core/res/res/layout/keyguard_screen_password_portrait.xml
index 994c439..6145e47 100644
--- a/core/res/res/layout/keyguard_screen_password_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_password_portrait.xml
@@ -114,7 +114,7 @@
             android:textAppearance="?android:attr/textAppearanceMedium"
             android:textColor="#ffffffff"
             android:imeOptions="actionDone"
-            android:suggestionsEnabled="false"/>
+            />
 
         <ImageView android:id="@+id/switch_ime_button"
             android:layout_width="wrap_content"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index bb61c72..a536961 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -881,10 +881,6 @@
      Default value is false. EditText content is always selectable. -->
     <attr name="textIsSelectable" format="boolean" />
 
-    <!-- When true, IME suggestions will be displayed when the user double taps on editable text.
-     The default value is true. -->
-    <attr name="suggestionsEnabled" format="boolean" />
-
     <!-- Where to ellipsize text. -->
     <attr name="ellipsize">
         <enum name="none" value="0" />
@@ -3148,8 +3144,6 @@
 
         <!-- Indicates that the content of a non-editable text can be selected. -->
         <attr name="textIsSelectable" />
-        <!-- Suggestions will be displayed when the user double taps on editable text. -->
-        <attr name="suggestionsEnabled" />
         <!-- Present the text in ALL CAPS. This may use a small-caps form when available. -->
         <attr name="textAllCaps" />
     </declare-styleable>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a6bf1e0..b9d05fd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1717,8 +1717,6 @@
   <public type="attr" name="textSuggestionsWindowStyle" />
   <public type="attr" name="textEditSuggestionItemLayout" />
 
-  <public type="attr" name="suggestionsEnabled" />
-
   <public type="attr" name="rowCount" />
   <public type="attr" name="rowOrderPreserved" />
   <public type="attr" name="columnCount" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8eaac8c..9455124 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2470,7 +2470,7 @@
     <string name="paste">Paste</string>
 
     <!-- Item on EditText context menu. This action is used to replace the current word by other suggested words, suggested by the IME or the spell checker -->
-    <string name="replace">Replace</string>
+    <string name="replace">Replace\u2026</string>
 
     <!-- Item on EditText context menu. This action is used to copy a URL from the edit field into the clipboard. -->
     <string name="copyUrl">Copy URL</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 94c6e41..d8d613a 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1854,7 +1854,7 @@
 
     <style name="Widget.Holo.ActionButton.Overflow">
         <item name="android:src">@android:drawable/ic_menu_moreoverflow_holo_dark</item>
-        <item name="android:background">?android:attr/selectableItemBackground</item>
+        <item name="android:background">?android:attr/actionBarItemBackground</item>
         <item name="android:contentDescription">@string/action_menu_overflow_description</item>
     </style>
 
diff --git a/data/sounds/AudioPackage7.mk b/data/sounds/AudioPackage7.mk
new file mode 100755
index 0000000..44dd899
--- /dev/null
+++ b/data/sounds/AudioPackage7.mk
@@ -0,0 +1,64 @@
+#
+# Audio Package 7 - Tuna
+# 
+# Include this file in a product makefile to include these audio files
+#
+# 
+
+LOCAL_PATH:= frameworks/base/data/sounds
+
+PRODUCT_COPY_FILES += \
+	$(LOCAL_PATH)/alarms/ogg/Cesium.ogg:system/media/audio/alarms/Cesium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:system/media/audio/alarms/Plutonium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Scandium.ogg:system/media/audio/alarms/Scandium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Curium.ogg:system/media/audio/alarms/Curium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Fermium.ogg:system/media/audio/alarms/Fermium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:system/media/audio/alarms/Nobelium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Copernicium.ogg:system/media/audio/alarms/Copernicium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Hassium.ogg:system/media/audio/alarms/Hassium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:system/media/audio/alarms/Neptunium.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard_24.ogg:system/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_24.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete_24.ogg:system/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn_24.ogg:system/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/ogg/CameraShutter.ogg:system/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Altair.ogg:system/media/audio/notifications/Altair.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Antares.ogg:system/media/audio/notifications/Antares.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Deneb.ogg:system/media/audio/notifications/Deneb.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:system/media/audio/notifications/Lalande.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:system/media/audio/notifications/Proxima.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Betelgeuse.ogg:system/media/audio/notifications/Betelgeuse.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:system/media/audio/notifications/Upsilon.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Bootes.ogg:system/media/audio/ringtones/Bootes.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:system/media/audio/ringtones/CanisMajor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Cassiopeia.ogg:system/media/audio/ringtones/Cassiopeia.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Lyra.ogg:system/media/audio/ringtones/Lyra.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:system/media/audio/ringtones/Perseus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Testudo.ogg:system/media/audio/ringtones/Testudo.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Vespa.ogg:system/media/audio/ringtones/Vespa.ogg
diff --git a/data/sounds/alarms/ogg/Copernicium.ogg b/data/sounds/alarms/ogg/Copernicium.ogg
new file mode 100644
index 0000000..c619e8b
--- /dev/null
+++ b/data/sounds/alarms/ogg/Copernicium.ogg
Binary files differ
diff --git a/data/sounds/alarms/ogg/Curium.ogg b/data/sounds/alarms/ogg/Curium.ogg
new file mode 100644
index 0000000..ebce391
--- /dev/null
+++ b/data/sounds/alarms/ogg/Curium.ogg
Binary files differ
diff --git a/data/sounds/alarms/ogg/Fermium.ogg b/data/sounds/alarms/ogg/Fermium.ogg
new file mode 100644
index 0000000..6132565
--- /dev/null
+++ b/data/sounds/alarms/ogg/Fermium.ogg
Binary files differ
diff --git a/data/sounds/alarms/ogg/Hassium.ogg b/data/sounds/alarms/ogg/Hassium.ogg
new file mode 100644
index 0000000..408b7c2
--- /dev/null
+++ b/data/sounds/alarms/ogg/Hassium.ogg
Binary files differ
diff --git a/data/sounds/alarms/ogg/Neptunium.ogg b/data/sounds/alarms/ogg/Neptunium.ogg
new file mode 100644
index 0000000..058e2db
--- /dev/null
+++ b/data/sounds/alarms/ogg/Neptunium.ogg
Binary files differ
diff --git a/data/sounds/alarms/ogg/Nobelium.ogg b/data/sounds/alarms/ogg/Nobelium.ogg
new file mode 100644
index 0000000..33878c9
--- /dev/null
+++ b/data/sounds/alarms/ogg/Nobelium.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/CameraShutter.ogg b/data/sounds/effects/ogg/CameraShutter.ogg
new file mode 100644
index 0000000..1b67dac
--- /dev/null
+++ b/data/sounds/effects/ogg/CameraShutter.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/Dock.ogg b/data/sounds/effects/ogg/Dock.ogg
old mode 100755
new mode 100644
index a1c1f2c..caa8eeb
--- a/data/sounds/effects/ogg/Dock.ogg
+++ b/data/sounds/effects/ogg/Dock.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressDelete_24.ogg b/data/sounds/effects/ogg/KeypressDelete_24.ogg
new file mode 100644
index 0000000..2503c3e
--- /dev/null
+++ b/data/sounds/effects/ogg/KeypressDelete_24.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressReturn_24.ogg b/data/sounds/effects/ogg/KeypressReturn_24.ogg
new file mode 100644
index 0000000..342eb12
--- /dev/null
+++ b/data/sounds/effects/ogg/KeypressReturn_24.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressSpacebar_24.ogg b/data/sounds/effects/ogg/KeypressSpacebar_24.ogg
new file mode 100644
index 0000000..9f17dd2
--- /dev/null
+++ b/data/sounds/effects/ogg/KeypressSpacebar_24.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressStandard_24.ogg b/data/sounds/effects/ogg/KeypressStandard_24.ogg
new file mode 100644
index 0000000..80d7d6d1
--- /dev/null
+++ b/data/sounds/effects/ogg/KeypressStandard_24.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/Lock.ogg b/data/sounds/effects/ogg/Lock.ogg
old mode 100755
new mode 100644
index deeba68..471258a
--- a/data/sounds/effects/ogg/Lock.ogg
+++ b/data/sounds/effects/ogg/Lock.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/Media_Volume.ogg b/data/sounds/effects/ogg/Media_Volume.ogg
index 88db9d9..b06656f 100644
--- a/data/sounds/effects/ogg/Media_Volume.ogg
+++ b/data/sounds/effects/ogg/Media_Volume.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/Undock.ogg b/data/sounds/effects/ogg/Undock.ogg
old mode 100755
new mode 100644
index 91e410e..28918f7
--- a/data/sounds/effects/ogg/Undock.ogg
+++ b/data/sounds/effects/ogg/Undock.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/Unlock.ogg b/data/sounds/effects/ogg/Unlock.ogg
old mode 100755
new mode 100644
index ac50288..1cd537b
--- a/data/sounds/effects/ogg/Unlock.ogg
+++ b/data/sounds/effects/ogg/Unlock.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/VideoRecord.ogg b/data/sounds/effects/ogg/VideoRecord.ogg
index 7afe9e6..28455c9 100644
--- a/data/sounds/effects/ogg/VideoRecord.ogg
+++ b/data/sounds/effects/ogg/VideoRecord.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Altair.ogg b/data/sounds/notifications/ogg/Altair.ogg
new file mode 100755
index 0000000..d84b59e
--- /dev/null
+++ b/data/sounds/notifications/ogg/Altair.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Antares.ogg b/data/sounds/notifications/ogg/Antares.ogg
new file mode 100755
index 0000000..9d60917
--- /dev/null
+++ b/data/sounds/notifications/ogg/Antares.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Betelgeuse.ogg b/data/sounds/notifications/ogg/Betelgeuse.ogg
new file mode 100644
index 0000000..83c7722
--- /dev/null
+++ b/data/sounds/notifications/ogg/Betelgeuse.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Deneb.ogg b/data/sounds/notifications/ogg/Deneb.ogg
new file mode 100755
index 0000000..e58b3b6
--- /dev/null
+++ b/data/sounds/notifications/ogg/Deneb.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Hojus.ogg b/data/sounds/notifications/ogg/Hojus.ogg
new file mode 100644
index 0000000..fc8f73f
--- /dev/null
+++ b/data/sounds/notifications/ogg/Hojus.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Lalande.ogg b/data/sounds/notifications/ogg/Lalande.ogg
new file mode 100755
index 0000000..b6e253a
--- /dev/null
+++ b/data/sounds/notifications/ogg/Lalande.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Mira.ogg b/data/sounds/notifications/ogg/Mira.ogg
new file mode 100644
index 0000000..f21e3c4
--- /dev/null
+++ b/data/sounds/notifications/ogg/Mira.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Proxima.ogg b/data/sounds/notifications/ogg/Proxima.ogg
new file mode 100644
index 0000000..235b5ca
--- /dev/null
+++ b/data/sounds/notifications/ogg/Proxima.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Upsilon.ogg b/data/sounds/notifications/ogg/Upsilon.ogg
new file mode 100644
index 0000000..036dcad
--- /dev/null
+++ b/data/sounds/notifications/ogg/Upsilon.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/Cassiopeia.ogg b/data/sounds/ringtones/ogg/Cassiopeia.ogg
index 942d4e4..61c4d27 100644
--- a/data/sounds/ringtones/ogg/Cassiopeia.ogg
+++ b/data/sounds/ringtones/ogg/Cassiopeia.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/Lyra.ogg b/data/sounds/ringtones/ogg/Lyra.ogg
index e4bf37a..b7f740d 100644
--- a/data/sounds/ringtones/ogg/Lyra.ogg
+++ b/data/sounds/ringtones/ogg/Lyra.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/Sceptrum.ogg b/data/sounds/ringtones/ogg/Sceptrum.ogg
index a006afe..89d64d70 100644
--- a/data/sounds/ringtones/ogg/Sceptrum.ogg
+++ b/data/sounds/ringtones/ogg/Sceptrum.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/Solarium.ogg b/data/sounds/ringtones/ogg/Solarium.ogg
index 108ba11..361367a 100644
--- a/data/sounds/ringtones/ogg/Solarium.ogg
+++ b/data/sounds/ringtones/ogg/Solarium.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/UrsaMinor.ogg b/data/sounds/ringtones/ogg/UrsaMinor.ogg
index 5591d73..a80801d 100644
--- a/data/sounds/ringtones/ogg/UrsaMinor.ogg
+++ b/data/sounds/ringtones/ogg/UrsaMinor.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/Vespa.ogg b/data/sounds/ringtones/ogg/Vespa.ogg
index 6fb8ebd..1f75ec8 100644
--- a/data/sounds/ringtones/ogg/Vespa.ogg
+++ b/data/sounds/ringtones/ogg/Vespa.ogg
Binary files differ
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index d62fd67..f3b62ec 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -93,7 +93,7 @@
      * @param texName the OpenGL texture object name (e.g. generated via glGenTextures)
      */
     public SurfaceTexture(int texName) {
-        this(texName, true);
+        this(texName, false);
     }
 
     /**
@@ -104,6 +104,8 @@
      *      When the image stream comes from OpenGL, SurfaceTexture may run in the synchronous
      *      mode where the producer side may be blocked to avoid skipping frames. To avoid the
      *      thread block, set allowSynchronousMode to false.
+     *
+     * @hide
      */
     public SurfaceTexture(int texName, boolean allowSynchronousMode) {
         Looper looper;
diff --git a/include/media/stagefright/SurfaceMediaSource.h b/include/media/stagefright/SurfaceMediaSource.h
index 1affb8a..74d54d1 100644
--- a/include/media/stagefright/SurfaceMediaSource.h
+++ b/include/media/stagefright/SurfaceMediaSource.h
@@ -182,9 +182,9 @@
 
 protected:
 
-    // freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for
+    // freeAllBuffersLocked frees the resources (both GraphicBuffer and EGLImage) for
     // all slots.
-    void freeAllBuffers();
+    void freeAllBuffersLocked();
     static bool isExternalFormat(uint32_t format);
 
 private:
@@ -337,8 +337,15 @@
     // Set to a default of 30 fps if not specified by the client side
     int32_t mFrameRate;
 
-    // mStarted is a flag to check if the recording has started
-    bool mStarted;
+    // mStopped is a flag to check if the recording is going on
+    bool mStopped;
+
+    // mNumFramesReceived indicates the number of frames recieved from
+    // the client side
+    int mNumFramesReceived;
+    // mNumFramesEncoded indicates the number of frames passed on to the
+    // encoder
+    int mNumFramesEncoded;
 
     // mFrameAvailableCondition condition used to indicate whether there
     // is a frame available for dequeuing
diff --git a/include/media/stagefright/openmax/OMX_IVCommon.h b/include/media/stagefright/openmax/OMX_IVCommon.h
index 97170d7..65b6339 100644
--- a/include/media/stagefright/openmax/OMX_IVCommon.h
+++ b/include/media/stagefright/openmax/OMX_IVCommon.h
@@ -154,7 +154,8 @@
      * Gralloc Buffers.
      * FIXME: In the process of reserving some enum values for
      * Android-specific OMX IL colorformats. Change this enum to
-     * an acceptable range once that is done.*/
+     * an acceptable range once that is done.
+     * */
     OMX_COLOR_FormatAndroidOpaque = 0x7F000001,
     OMX_TI_COLOR_FormatYUV420PackedSemiPlanar = 0x7F000100,
     OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00,
diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h
index 370253a..b9deafc 100644
--- a/include/ui/GraphicBuffer.h
+++ b/include/ui/GraphicBuffer.h
@@ -62,6 +62,7 @@
         USAGE_HW_TEXTURE        = GRALLOC_USAGE_HW_TEXTURE,
         USAGE_HW_RENDER         = GRALLOC_USAGE_HW_RENDER,
         USAGE_HW_2D             = GRALLOC_USAGE_HW_2D,
+        USAGE_HW_COMPOSER       = GRALLOC_USAGE_HW_COMPOSER,
         USAGE_HW_MASK           = GRALLOC_USAGE_HW_MASK
     };
 
diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp
index 36bbdf0..d6ab0da 100644
--- a/libs/rs/rsElement.cpp
+++ b/libs/rs/rsElement.cpp
@@ -284,6 +284,15 @@
     }
 }
 
+Element::Builder::Builder() {
+    const uint32_t initialCapacity = 32;
+    mBuilderElementRefs.setCapacity(initialCapacity);
+    mBuilderElements.setCapacity(initialCapacity);
+    mBuilderNameStrings.setCapacity(initialCapacity);
+    mBuilderNameLengths.setCapacity(initialCapacity);
+    mBuilderArrays.setCapacity(initialCapacity);
+}
+
 void Element::Builder::add(const Element *e, const char *nameStr, uint32_t arraySize) {
     mBuilderElementRefs.push(ObjectBaseRef<const Element>(e));
     mBuilderElements.push(e);
@@ -303,41 +312,12 @@
 
 
 ElementState::ElementState() {
-    const uint32_t initialCapacity = 32;
-    mBuilderElements.setCapacity(initialCapacity);
-    mBuilderNameStrings.setCapacity(initialCapacity);
-    mBuilderNameLengths.setCapacity(initialCapacity);
-    mBuilderArrays.setCapacity(initialCapacity);
 }
 
 ElementState::~ElementState() {
     rsAssert(!mElements.size());
 }
 
-void ElementState::elementBuilderBegin() {
-    mBuilderElements.clear();
-    mBuilderNameStrings.clear();
-    mBuilderNameLengths.clear();
-    mBuilderArrays.clear();
-}
-
-void ElementState::elementBuilderAdd(const Element *e, const char *nameStr, uint32_t arraySize) {
-    mBuilderElements.push(e);
-    mBuilderNameStrings.push(nameStr);
-    mBuilderNameLengths.push(strlen(nameStr));
-    mBuilderArrays.push(arraySize);
-
-}
-
-const Element *ElementState::elementBuilderCreate(Context *rsc) {
-    return Element::create(rsc, mBuilderElements.size(),
-                           &(mBuilderElements.editArray()[0]),
-                           &(mBuilderNameStrings.editArray()[0]),
-                           mBuilderNameLengths.editArray(),
-                           mBuilderArrays.editArray());
-}
-
-
 /////////////////////////////////////////
 //
 
diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h
index c3ef250..bfdec53 100644
--- a/libs/rs/rsElement.h
+++ b/libs/rs/rsElement.h
@@ -30,6 +30,7 @@
 public:
     class Builder {
     public:
+        Builder();
         void add(const Element *e, const char *nameStr, uint32_t arraySize);
         ObjectBaseRef<const Element> create(Context *rsc);
     private:
@@ -135,17 +136,8 @@
     ElementState();
     ~ElementState();
 
-    void elementBuilderBegin();
-    void elementBuilderAdd(const Element *e, const char *nameStr, uint32_t arraySize);
-    const Element *elementBuilderCreate(Context *rsc);
-
     // Cache of all existing elements.
     Vector<Element *> mElements;
-private:
-    Vector<const Element *> mBuilderElements;
-    Vector<const char*> mBuilderNameStrings;
-    Vector<size_t> mBuilderNameLengths;
-    Vector<uint32_t> mBuilderArrays;
 };
 
 
diff --git a/libs/rs/rsLocklessFifo.cpp b/libs/rs/rsLocklessFifo.cpp
index 02a76ab..4d02269 100644
--- a/libs/rs/rsLocklessFifo.cpp
+++ b/libs/rs/rsLocklessFifo.cpp
@@ -21,11 +21,11 @@
 using namespace android;
 using namespace android::renderscript;
 
-LocklessCommandFifo::LocklessCommandFifo() : mBuffer(0) {
+LocklessCommandFifo::LocklessCommandFifo() : mBuffer(0), mInitialized(false) {
 }
 
 LocklessCommandFifo::~LocklessCommandFifo() {
-    if (!mInShutdown) {
+    if (!mInShutdown && mInitialized) {
         shutdown();
     }
     if (mBuffer) {
@@ -58,6 +58,7 @@
     mGet = mBuffer;
     mEnd = mBuffer + (sizeInBytes) - 1;
     //dumpState("init");
+    mInitialized = true;
     return true;
 }
 
diff --git a/libs/rs/rsLocklessFifo.h b/libs/rs/rsLocklessFifo.h
index 4962ef6..fa53d40 100644
--- a/libs/rs/rsLocklessFifo.h
+++ b/libs/rs/rsLocklessFifo.h
@@ -47,6 +47,7 @@
     uint8_t * mEnd;
     uint8_t mSize;
     bool mInShutdown;
+    bool mInitialized;
 
     Signal mSignalToWorker;
     Signal mSignalToControl;
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index acc2b23..fe57e8a 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -3072,11 +3072,13 @@
     /**
      * Update the remote control displays with the new "focused" client generation
      */
-    private void setNewRcClientGenerationOnDisplays_syncRcStack(int newClientGeneration) {
+    private void setNewRcClientOnDisplays_syncAfRcsCurrc(int newClientGeneration,
+            ComponentName newClientEventReceiver, boolean clearing) {
         // NOTE: Only one IRemoteControlDisplay supported in this implementation
         if (mRcDisplay != null) {
             try {
-                mRcDisplay.setCurrentClientGenerationId(newClientGeneration);
+                mRcDisplay.setCurrentClientId(
+                        newClientGeneration, newClientEventReceiver, clearing);
             } catch (RemoteException e) {
                 Log.e(TAG, "Dead display in onRcDisplayUpdate() "+e);
                 // if we had a display before, stop monitoring its death
@@ -3089,7 +3091,7 @@
     /**
      * Update the remote control clients with the new "focused" client generation
      */
-    private void setNewRcClientGenerationOnClients_syncRcStack(int newClientGeneration) {
+    private void setNewRcClientGenerationOnClients_syncAfRcsCurrc(int newClientGeneration) {
         Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
         while(stackIterator.hasNext()) {
             RemoteControlStackEntry se = stackIterator.next();
@@ -3106,15 +3108,20 @@
     }
 
     /**
-     * Update the displays and clients with the new "focused" client generation
+     * Update the displays and clients with the new "focused" client generation and name
+     * @param newClientGeneration the new generation value matching a client update
+     * @param newClientEventReceiver the media button event receiver associated with the client.
+     *    May be null, which implies there is no registered media button event receiver.
+     * @param clearing true if the new client generation value maps to a remote control update
+     *    where the display should be cleared.
      */
-    private void setNewRcClientGeneration(int newClientGeneration) {
-        synchronized(mRCStack) {
-            // send the new valid client generation ID to all displays
-            setNewRcClientGenerationOnDisplays_syncRcStack(newClientGeneration);
-            // send the new valid client generation ID to all clients
-            setNewRcClientGenerationOnClients_syncRcStack(newClientGeneration);
-        }
+    private void setNewRcClient_syncAfRcsCurrc(int newClientGeneration,
+            ComponentName newClientEventReceiver, boolean clearing) {
+        // send the new valid client generation ID to all displays
+        setNewRcClientOnDisplays_syncAfRcsCurrc(newClientGeneration, newClientEventReceiver,
+                clearing);
+        // send the new valid client generation ID to all clients
+        setNewRcClientGenerationOnClients_syncAfRcsCurrc(newClientGeneration);
     }
 
     /**
@@ -3124,11 +3131,13 @@
         // TODO remove log before release
         Log.i(TAG, "Clear remote control display");
 
-        synchronized(mCurrentRcLock) {
-            mCurrentRcClientGen++;
-
-            // synchronously update the displays and clients with the new client generation
-            setNewRcClientGeneration(mCurrentRcClientGen);
+        synchronized(mRCStack) {
+            synchronized(mCurrentRcLock) {
+                mCurrentRcClientGen++;
+                // synchronously update the displays and clients with the new client generation
+                setNewRcClient_syncAfRcsCurrc(mCurrentRcClientGen,
+                        null /*event receiver*/, true /*clearing*/);
+            }
         }
     }
 
@@ -3136,28 +3145,32 @@
      * Called when processing MSG_RCDISPLAY_UPDATE event
      */
     private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) {
-        synchronized(mCurrentRcLock) {
-            if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
-                // TODO remove log before release
-                Log.i(TAG, "Display/update remote control ");
+        synchronized(mRCStack) {
+            synchronized(mCurrentRcLock) {
+                if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
+                    // TODO remove log before release
+                    Log.i(TAG, "Display/update remote control ");
 
-                mCurrentRcClientGen++;
+                    mCurrentRcClientGen++;
+                    // synchronously update the displays and clients with
+                    //      the new client generation
+                    setNewRcClient_syncAfRcsCurrc(mCurrentRcClientGen,
+                            rcse.mReceiverComponent /*event receiver*/,
+                            false /*clearing*/);
 
-                // synchronously update the displays and clients with the new client generation
-                setNewRcClientGeneration(mCurrentRcClientGen);
-
-                // ask the current client that it needs to send info
-                try {
-                    mCurrentRcClient.onInformationRequested(mCurrentRcClientGen,
-                            flags, mArtworkExpectedWidth, mArtworkExpectedHeight);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Current valid remote client is dead: "+e);
-                    mCurrentRcClient = null;
+                    // ask the current client that it needs to send info
+                    try {
+                        mCurrentRcClient.onInformationRequested(mCurrentRcClientGen,
+                                flags, mArtworkExpectedWidth, mArtworkExpectedHeight);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Current valid remote client is dead: "+e);
+                        mCurrentRcClient = null;
+                    }
+                } else {
+                    // the remote control display owner has changed between the
+                    // the message to update the display was sent, and the time it
+                    // gets to be processed (now)
                 }
-            } else {
-                // the remote control display owner has changed between the
-                // the message to update the display was sent, and the time it
-                // gets to be processed (now)
             }
         }
     }
@@ -3320,10 +3333,31 @@
         }
     }
 
-    /** see AudioManager.unregisterRemoteControlClient(ComponentName eventReceiver, ...) */
+    /**
+     * see AudioManager.unregisterRemoteControlClient(ComponentName eventReceiver, ...)
+     * rcClient is guaranteed non-null
+     */
     public void unregisterRemoteControlClient(ComponentName eventReceiver,
             IRemoteControlClient rcClient) {
-        //FIXME implement
+        synchronized(mAudioFocusLock) {
+            synchronized(mRCStack) {
+                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+                while(stackIterator.hasNext()) {
+                    RemoteControlStackEntry rcse = stackIterator.next();
+                    if ((rcse.mReceiverComponent.equals(eventReceiver))
+                            && rcClient.equals(rcse.mRcClient)) {
+                        // we found the IRemoteControlClient to unregister
+                        // stop monitoring its death
+                        rcse.unlinkToRcClientDeath();
+                        // reset the client-related fields
+                        rcse.mRcClient = null;
+                        rcse.mRcClientName = null;
+                        rcse.mRcClientDeathHandler = null;
+                        rcse.mCallingPackageName = null;
+                    }
+                }
+            }
+        }
     }
 
     /**
diff --git a/media/java/android/media/IRemoteControlDisplay.aidl b/media/java/android/media/IRemoteControlDisplay.aidl
index d000906..fd50b7e 100644
--- a/media/java/android/media/IRemoteControlDisplay.aidl
+++ b/media/java/android/media/IRemoteControlDisplay.aidl
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.content.ComponentName;
 import android.graphics.Bitmap;
 import android.os.Bundle;
 
@@ -29,15 +30,23 @@
 {
     /**
      * Sets the generation counter of the current client that is displayed on the remote control.
+     * @param clientGeneration the new RemoteControlClient generation
+     * @param clientEventReceiver the media button event receiver associated with the client.
+     *    May be null, which implies there is no registered media button event receiver. This
+     *    parameter is supplied as an optimization so a display can directly target media button
+     *    events to the client.
+     * @param clearing true if the new client generation value maps to a remote control update
+     *    where the display should be cleared.
      */
-    void setCurrentClientGenerationId(int clientGeneration);
+    void setCurrentClientId(int clientGeneration, in ComponentName clientEventReceiver,
+            boolean clearing);
 
     void setPlaybackState(int generationId, int state);
 
-    void setMetadata(int generationId, in Bundle metadata);
-
     void setTransportControlFlags(int generationId, int transportControlFlags);
 
+    void setMetadata(int generationId, in Bundle metadata);
+
     void setArtwork(int generationId, in Bitmap artwork);
 
     /**
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 27dfeab..7f09319 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -808,7 +808,7 @@
         }
 
         if (format.eCompressionFormat == compressionFormat
-            && format.eColorFormat == colorFormat) {
+                && format.eColorFormat == colorFormat) {
             found = true;
             break;
         }
@@ -838,6 +838,15 @@
         case OMX_COLOR_FormatYUV420Planar:
         case OMX_COLOR_FormatYUV420SemiPlanar:
         case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar:
+        /*
+        * FIXME: For the Opaque color format, the frame size does not
+        * need to be (w*h*3)/2. It just needs to
+        * be larger than certain minimum buffer size. However,
+        * currently, this opaque foramt has been tested only on
+        * YUV420 formats. If that is changed, then we need to revisit
+        * this part in the future
+        */
+        case OMX_COLOR_FormatAndroidOpaque:
             return (width * height * 3) / 2;
 
         default:
@@ -887,7 +896,7 @@
         // Make sure that omx component does not overwrite
         // the incremented index (bug 2897413).
         CHECK_EQ(index, portFormat.nIndex);
-        if ((portFormat.eColorFormat == colorFormat)) {
+        if (portFormat.eColorFormat == colorFormat) {
             LOGV("Found supported color format: %d", portFormat.eColorFormat);
             return OK;  // colorFormat is supported!
         }
@@ -2316,6 +2325,7 @@
         {
             CODEC_LOGV("OMX_EventPortSettingsChanged(port=%ld, data2=0x%08lx)",
                        data1, data2);
+            CHECK(mFilledBuffers.empty());
 
             if (data2 == 0 || data2 == OMX_IndexParamPortDefinition) {
                 onPortSettingsChanged(data1);
@@ -2923,6 +2933,7 @@
     size_t offset = 0;
     int32_t n = 0;
 
+
     for (;;) {
         MediaBuffer *srcBuffer;
         if (mSeekTimeUs >= 0) {
@@ -3021,6 +3032,7 @@
                 CHECK(info->mMediaBuffer == NULL);
                 info->mMediaBuffer = srcBuffer;
             } else {
+                CHECK(srcBuffer->data() != NULL) ;
                 memcpy((uint8_t *)info->mData + offset,
                         (const uint8_t *)srcBuffer->data()
                             + srcBuffer->range_offset(),
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index ddfd9ff..c2e6707 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 // #define LOG_NDEBUG 0
 #define LOG_TAG "SurfaceMediaSource"
 
@@ -47,7 +46,9 @@
                 mSynchronousMode(true),
                 mConnectedApi(NO_CONNECTED_API),
                 mFrameRate(30),
-                mStarted(false)   {
+                mNumFramesReceived(0),
+                mNumFramesEncoded(0),
+                mStopped(false) {
     LOGV("SurfaceMediaSource::SurfaceMediaSource");
     sp<ISurfaceComposer> composer(ComposerService::getComposerService());
     mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
@@ -55,10 +56,9 @@
 
 SurfaceMediaSource::~SurfaceMediaSource() {
     LOGV("SurfaceMediaSource::~SurfaceMediaSource");
-    if (mStarted) {
+    if (!mStopped) {
         stop();
     }
-    freeAllBuffers();
 }
 
 size_t SurfaceMediaSource::getQueuedCount() const {
@@ -139,12 +139,12 @@
 
     // here we're guaranteed that the client doesn't have dequeued buffers
     // and will release all of its buffer references.
-    freeAllBuffers();
     mBufferCount = bufferCount;
     mClientBufferCount = bufferCount;
     mCurrentSlot = INVALID_BUFFER_SLOT;
     mQueue.clear();
     mDequeueCondition.signal();
+    freeAllBuffersLocked();
     return OK;
 }
 
@@ -164,7 +164,7 @@
 status_t SurfaceMediaSource::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
                                             uint32_t format, uint32_t usage) {
     LOGV("dequeueBuffer");
-
+    Mutex::Autolock lock(mMutex);
 
     // Check for the buffer size- the client should just use the
     // default width and height, and not try to set those.
@@ -184,10 +184,7 @@
         return BAD_VALUE;
     }
 
-    Mutex::Autolock lock(mMutex);
-
     status_t returnFlags(OK);
-
     int found, foundSync;
     int dequeuedCount = 0;
     bool tryAgain = true;
@@ -218,6 +215,9 @@
                 LOGV("Waiting for the FIFO to drain");
                 mDequeueCondition.wait(mMutex);
             }
+            if (mStopped) {
+                return NO_INIT;
+            }
             // need to check again since the mode could have changed
             // while we were waiting
             minBufferCountNeeded = mSynchronousMode ?
@@ -228,7 +228,7 @@
                 ((mServerBufferCount != mBufferCount) ||
                         (mServerBufferCount < minBufferCountNeeded))) {
             // here we're guaranteed that mQueue is empty
-            freeAllBuffers();
+            freeAllBuffersLocked();
             mBufferCount = mServerBufferCount;
             if (mBufferCount < minBufferCountNeeded)
                 mBufferCount = minBufferCountNeeded;
@@ -290,9 +290,12 @@
         // for for some buffers to be consumed
         tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT);
         if (tryAgain) {
-            LOGW("Waiting..In synchronous mode and no buffer to dQ");
+            LOGV("Waiting..In synchronous mode and no buffer to dequeue");
             mDequeueCondition.wait(mMutex);
         }
+        if (mStopped) {
+            return NO_INIT;
+        }
     }
 
     if (mSynchronousMode && found == INVALID_BUFFER_SLOT) {
@@ -304,7 +307,7 @@
         return -EBUSY;
     }
 
-    const int buf = found;
+    const int bufIndex = found;
     *outBuf = found;
 
     const bool useDefaultSize = !w && !h;
@@ -322,9 +325,9 @@
 
     // buffer is now in DEQUEUED (but can also be current at the same time,
     // if we're in synchronous mode)
-    mSlots[buf].mBufferState = BufferSlot::DEQUEUED;
+    mSlots[bufIndex].mBufferState = BufferSlot::DEQUEUED;
 
-    const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer);
+    const sp<GraphicBuffer>& buffer(mSlots[bufIndex].mGraphicBuffer);
     if ((buffer == NULL) ||
         (uint32_t(buffer->width)  != w) ||
         (uint32_t(buffer->height) != h) ||
@@ -342,22 +345,25 @@
             if (updateFormat) {
                 mPixelFormat = format;
             }
-            mSlots[buf].mGraphicBuffer = graphicBuffer;
-            mSlots[buf].mRequestBufferCalled = false;
+            mSlots[bufIndex].mGraphicBuffer = graphicBuffer;
+            mSlots[bufIndex].mRequestBufferCalled = false;
             returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
     }
     return returnFlags;
 }
 
+// TODO: clean this up
 status_t SurfaceMediaSource::setSynchronousMode(bool enabled) {
     Mutex::Autolock lock(mMutex);
+    if (mStopped) {
+        LOGE("setSynchronousMode: SurfaceMediaSource has been stopped!");
+        return NO_INIT;
+    }
 
-    status_t err = OK;
     if (!enabled) {
-        // going to asynchronous mode, drain the queue
-        while (mSynchronousMode != enabled && !mQueue.isEmpty()) {
-            mDequeueCondition.wait(mMutex);
-        }
+        // Async mode is not allowed
+        LOGE("SurfaceMediaSource can be used only synchronous mode!");
+        return INVALID_OPERATION;
     }
 
     if (mSynchronousMode != enabled) {
@@ -368,13 +374,19 @@
         mSynchronousMode = enabled;
         mDequeueCondition.signal();
     }
-    return err;
+    return OK;
 }
 
 status_t SurfaceMediaSource::connect(int api,
         uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
     LOGV("SurfaceMediaSource::connect");
     Mutex::Autolock lock(mMutex);
+
+    if (mStopped) {
+        LOGE("Connect: SurfaceMediaSource has been stopped!");
+        return NO_INIT;
+    }
+
     status_t err = NO_ERROR;
     switch (api) {
         case NATIVE_WINDOW_API_EGL:
@@ -397,9 +409,25 @@
     return err;
 }
 
+// This is called by the client side when it is done
+// TODO: Currently, this also sets mStopped to true which
+// is needed for unblocking the encoder which might be
+// waiting to read more frames. So if on the client side,
+// the same thread supplies the frames and also calls stop
+// on the encoder, the client has to call disconnect before
+// it calls stop.
+// In the case of the camera,
+// that need not be required since the thread supplying the
+// frames is separate than the one calling stop.
 status_t SurfaceMediaSource::disconnect(int api) {
     LOGV("SurfaceMediaSource::disconnect");
     Mutex::Autolock lock(mMutex);
+
+    if (mStopped) {
+        LOGE("disconnect: SurfaceMediaSoource is already stopped!");
+        return NO_INIT;
+    }
+
     status_t err = NO_ERROR;
     switch (api) {
         case NATIVE_WINDOW_API_EGL:
@@ -408,6 +436,9 @@
         case NATIVE_WINDOW_API_CAMERA:
             if (mConnectedApi == api) {
                 mConnectedApi = NO_CONNECTED_API;
+                mStopped = true;
+                mDequeueCondition.signal();
+                mFrameAvailableCondition.signal();
             } else {
                 err = -EINVAL;
             }
@@ -419,45 +450,47 @@
     return err;
 }
 
-status_t SurfaceMediaSource::queueBuffer(int buf, int64_t timestamp,
+status_t SurfaceMediaSource::queueBuffer(int bufIndex, int64_t timestamp,
         uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
     LOGV("queueBuffer");
 
     Mutex::Autolock lock(mMutex);
-    if (buf < 0 || buf >= mBufferCount) {
+    if (bufIndex < 0 || bufIndex >= mBufferCount) {
         LOGE("queueBuffer: slot index out of range [0, %d]: %d",
-                mBufferCount, buf);
+                mBufferCount, bufIndex);
         return -EINVAL;
-    } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+    } else if (mSlots[bufIndex].mBufferState != BufferSlot::DEQUEUED) {
         LOGE("queueBuffer: slot %d is not owned by the client (state=%d)",
-                buf, mSlots[buf].mBufferState);
+                bufIndex, mSlots[bufIndex].mBufferState);
         return -EINVAL;
-    } else if (!mSlots[buf].mRequestBufferCalled) {
+    } else if (!mSlots[bufIndex].mRequestBufferCalled) {
         LOGE("queueBuffer: slot %d was enqueued without requesting a "
-                "buffer", buf);
+                "buffer", bufIndex);
         return -EINVAL;
     }
 
     if (mSynchronousMode) {
         // in synchronous mode we queue all buffers in a FIFO
-        mQueue.push_back(buf);
-        LOGV("Client queued buffer on slot: %d, Q size = %d",
-                                                buf, mQueue.size());
+        mQueue.push_back(bufIndex);
+        mNumFramesReceived++;
+        LOGV("Client queued buf# %d @slot: %d, Q size = %d, handle = %p, timestamp = %lld",
+            mNumFramesReceived, bufIndex, mQueue.size(),
+            mSlots[bufIndex].mGraphicBuffer->handle, timestamp);
     } else {
         // in asynchronous mode we only keep the most recent buffer
         if (mQueue.empty()) {
-            mQueue.push_back(buf);
+            mQueue.push_back(bufIndex);
         } else {
             Fifo::iterator front(mQueue.begin());
             // buffer currently queued is freed
             mSlots[*front].mBufferState = BufferSlot::FREE;
             // and we record the new buffer index in the queued list
-            *front = buf;
+            *front = bufIndex;
         }
     }
 
-    mSlots[buf].mBufferState = BufferSlot::QUEUED;
-    mSlots[buf].mTimestamp = timestamp;
+    mSlots[bufIndex].mBufferState = BufferSlot::QUEUED;
+    mSlots[bufIndex].mTimestamp = timestamp;
     // TODO: (Confirm) Don't want to signal dequeue here.
     // May be just in asynchronous mode?
     // mDequeueCondition.signal();
@@ -482,7 +515,7 @@
 // wait to hear from StageFrightRecorder to set the buffer FREE
 // Make sure this is called when the mutex is locked
 status_t SurfaceMediaSource::onFrameReceivedLocked() {
-    LOGV("On Frame Received");
+    LOGV("On Frame Received locked");
     // Signal the encoder that a new frame has arrived
     mFrameAvailableCondition.signal();
 
@@ -501,19 +534,19 @@
 }
 
 
-void SurfaceMediaSource::cancelBuffer(int buf) {
+void SurfaceMediaSource::cancelBuffer(int bufIndex) {
     LOGV("SurfaceMediaSource::cancelBuffer");
     Mutex::Autolock lock(mMutex);
-    if (buf < 0 || buf >= mBufferCount) {
+    if (bufIndex < 0 || bufIndex >= mBufferCount) {
         LOGE("cancelBuffer: slot index out of range [0, %d]: %d",
-                mBufferCount, buf);
+                mBufferCount, bufIndex);
         return;
-    } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+    } else if (mSlots[bufIndex].mBufferState != BufferSlot::DEQUEUED) {
         LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)",
-                buf, mSlots[buf].mBufferState);
+                bufIndex, mSlots[bufIndex].mBufferState);
         return;
     }
-    mSlots[buf].mBufferState = BufferSlot::FREE;
+    mSlots[bufIndex].mBufferState = BufferSlot::FREE;
     mDequeueCondition.signal();
 }
 
@@ -531,8 +564,8 @@
     mFrameAvailableListener = listener;
 }
 
-void SurfaceMediaSource::freeAllBuffers() {
-    LOGV("freeAllBuffers");
+void SurfaceMediaSource::freeAllBuffersLocked() {
+    LOGV("freeAllBuffersLocked");
     for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
         mSlots[i].mGraphicBuffer = 0;
         mSlots[i].mBufferState = BufferSlot::FREE;
@@ -648,10 +681,7 @@
 
 status_t SurfaceMediaSource::start(MetaData *params)
 {
-    LOGV("start");
-    Mutex::Autolock lock(mMutex);
-    CHECK(!mStarted);
-    mStarted = true;
+    LOGV("started!");
     return OK;
 }
 
@@ -662,8 +692,11 @@
 
     Mutex::Autolock lock(mMutex);
     // TODO: Add waiting on mFrameCompletedCondition here?
-    mStarted = false;
+    mStopped = true;
     mFrameAvailableCondition.signal();
+    mDequeueCondition.signal();
+    mQueue.clear();
+    freeAllBuffersLocked();
 
     return OK;
 }
@@ -688,23 +721,25 @@
 }
 
 status_t SurfaceMediaSource::read( MediaBuffer **buffer,
-                                const ReadOptions *options)
+                                    const ReadOptions *options)
 {
+    Mutex::Autolock autoLock(mMutex) ;
+
     LOGV("Read. Size of queued buffer: %d", mQueue.size());
     *buffer = NULL;
 
-    Mutex::Autolock autoLock(mMutex) ;
     // If the recording has started and the queue is empty, then just
     // wait here till the frames come in from the client side
-    while (mStarted && mQueue.empty()) {
+    while (!mStopped && mQueue.empty()) {
         LOGV("NO FRAMES! Recorder waiting for FrameAvailableCondition");
         mFrameAvailableCondition.wait(mMutex);
     }
 
     // If the loop was exited as a result of stopping the recording,
     // it is OK
-    if (!mStarted) {
-        return OK;
+    if (mStopped) {
+        LOGV("Read: SurfaceMediaSource is stopped. Returning NO_INIT;");
+        return NO_INIT;
     }
 
     // Update the current buffer info
@@ -712,15 +747,20 @@
     // can be more than one "current" slots.
     Fifo::iterator front(mQueue.begin());
     mCurrentSlot = *front;
+    mQueue.erase(front);
     mCurrentBuf = mSlots[mCurrentSlot].mGraphicBuffer;
+    int64_t prevTimeStamp = mCurrentTimestamp;
     mCurrentTimestamp = mSlots[mCurrentSlot].mTimestamp;
-
+    mNumFramesEncoded++;
     // Pass the data to the MediaBuffer. Pass in only the metadata
     passMetadataBufferLocked(buffer);
 
     (*buffer)->setObserver(this);
     (*buffer)->add_ref();
-    (*buffer)->meta_data()->setInt64(kKeyTime, mCurrentTimestamp);
+    (*buffer)->meta_data()->setInt64(kKeyTime, mCurrentTimestamp / 1000);
+    LOGV("Frames encoded = %d, timestamp = %lld, time diff = %lld",
+            mNumFramesEncoded, mCurrentTimestamp / 1000,
+            mCurrentTimestamp / 1000 - prevTimeStamp / 1000);
 
     return OK;
 }
@@ -743,15 +783,17 @@
         new MediaBuffer(4 + sizeof(buffer_handle_t));
     char *data = (char *)tempBuffer->data();
     if (data == NULL) {
-        LOGE("Cannot allocate memory for passing buffer metadata!");
+        LOGE("Cannot allocate memory for metadata buffer!");
         return;
     }
     OMX_U32 type = kMetadataBufferTypeGrallocSource;
     memcpy(data, &type, 4);
     memcpy(data + 4, &(mCurrentBuf->handle), sizeof(buffer_handle_t));
     *buffer = tempBuffer;
-}
 
+    LOGV("handle = %p, , offset = %d, length = %d",
+            mCurrentBuf->handle, (*buffer)->range_length(), (*buffer)->range_offset());
+}
 
 void SurfaceMediaSource::signalBufferReturned(MediaBuffer *buffer) {
     LOGV("signalBufferReturned");
@@ -759,16 +801,19 @@
     bool foundBuffer = false;
     Mutex::Autolock autoLock(mMutex);
 
-    if (!mStarted) {
-        LOGW("signalBufferReturned: mStarted = false! Nothing to do!");
+    if (mStopped) {
+        LOGV("signalBufferReturned: mStopped = true! Nothing to do!");
         return;
     }
 
-    for (Fifo::iterator it = mQueue.begin(); it != mQueue.end(); ++it) {
-        CHECK(mSlots[*it].mGraphicBuffer != NULL);
-        if (checkBufferMatchesSlot(*it, buffer)) {
-            mSlots[*it].mBufferState = BufferSlot::FREE;
-            mQueue.erase(it);
+    for (int id = 0; id < NUM_BUFFER_SLOTS; id++) {
+        if (mSlots[id].mGraphicBuffer == NULL) {
+            continue;
+        }
+        if (checkBufferMatchesSlot(id, buffer)) {
+            LOGV("Slot %d returned, matches handle = %p", id,
+                    mSlots[id].mGraphicBuffer->handle);
+            mSlots[id].mBufferState = BufferSlot::FREE;
             buffer->setObserver(0);
             buffer->release();
             mDequeueCondition.signal();
@@ -792,5 +837,4 @@
     return mSlots[slot].mGraphicBuffer->handle  ==  bufferHandle;
 }
 
-
 } // end of namespace android
diff --git a/media/libstagefright/tests/Android.mk b/media/libstagefright/tests/Android.mk
index 3ea8f39..357feb1 100644
--- a/media/libstagefright/tests/Android.mk
+++ b/media/libstagefright/tests/Android.mk
@@ -19,12 +19,13 @@
 	libbinder \
 	libcutils \
 	libgui \
-	libstlport \
-	libui \
-	libutils \
+	libmedia \
 	libstagefright \
 	libstagefright_omx \
 	libstagefright_foundation \
+	libstlport \
+	libui \
+	libutils \
 
 LOCAL_STATIC_LIBRARIES := \
 	libgtest \
diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
index 5b32b68..d643a0b 100644
--- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp
+++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
@@ -14,14 +14,17 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "SurfaceMediaSource_test"
 // #define LOG_NDEBUG 0
+#define LOG_TAG "SurfaceMediaSource_test"
 
 #include <gtest/gtest.h>
 #include <utils/String8.h>
 #include <utils/Errors.h>
+#include <fcntl.h>
+#include <unistd.h>
 
 #include <media/stagefright/SurfaceMediaSource.h>
+#include <media/mediarecorder.h>
 
 #include <gui/SurfaceTextureClient.h>
 #include <ui/GraphicBuffer.h>
@@ -33,24 +36,322 @@
 #include <ui/FramebufferNativeWindow.h>
 
 #include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaBufferGroup.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaData.h>
-#include <media/stagefright/MPEG4Writer.h>
 #include <media/stagefright/OMXClient.h>
 #include <media/stagefright/OMXCodec.h>
 #include <OMX_Component.h>
 
 #include "DummyRecorder.h"
 
+
 namespace android {
 
+class GLTest : public ::testing::Test {
+protected:
 
+    GLTest():
+            mEglDisplay(EGL_NO_DISPLAY),
+            mEglSurface(EGL_NO_SURFACE),
+            mEglContext(EGL_NO_CONTEXT) {
+    }
+
+    virtual void SetUp() {
+        LOGV("GLTest::SetUp()");
+        mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
+
+        EGLint majorVersion;
+        EGLint minorVersion;
+        EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        RecordProperty("EglVersionMajor", majorVersion);
+        RecordProperty("EglVersionMajor", minorVersion);
+
+        EGLint numConfigs = 0;
+        EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mGlConfig,
+                1, &numConfigs));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+        char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS");
+        if (displaySecsEnv != NULL) {
+            mDisplaySecs = atoi(displaySecsEnv);
+            if (mDisplaySecs < 0) {
+                mDisplaySecs = 0;
+            }
+        } else {
+            mDisplaySecs = 0;
+        }
+
+        if (mDisplaySecs > 0) {
+            mComposerClient = new SurfaceComposerClient;
+            ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+            mSurfaceControl = mComposerClient->createSurface(
+                    String8("Test Surface"), 0,
+                    getSurfaceWidth(), getSurfaceHeight(),
+                    PIXEL_FORMAT_RGB_888, 0);
+
+            ASSERT_TRUE(mSurfaceControl != NULL);
+            ASSERT_TRUE(mSurfaceControl->isValid());
+
+            SurfaceComposerClient::openGlobalTransaction();
+            ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF));
+            ASSERT_EQ(NO_ERROR, mSurfaceControl->show());
+            SurfaceComposerClient::closeGlobalTransaction();
+
+            sp<ANativeWindow> window = mSurfaceControl->getSurface();
+            mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
+                    window.get(), NULL);
+        } else {
+            EGLint pbufferAttribs[] = {
+                EGL_WIDTH, getSurfaceWidth(),
+                EGL_HEIGHT, getSurfaceHeight(),
+                EGL_NONE };
+
+            mEglSurface = eglCreatePbufferSurface(mEglDisplay, mGlConfig,
+                    pbufferAttribs);
+        }
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
+
+        mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT,
+                getContextAttribs());
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
+
+        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+                mEglContext));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+        EGLint w, h;
+        EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &w));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &h));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        RecordProperty("EglSurfaceWidth", w);
+        RecordProperty("EglSurfaceHeight", h);
+
+        glViewport(0, 0, w, h);
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+    }
+
+    virtual void TearDown() {
+        // Display the result
+        if (mDisplaySecs > 0 && mEglSurface != EGL_NO_SURFACE) {
+            eglSwapBuffers(mEglDisplay, mEglSurface);
+            sleep(mDisplaySecs);
+        }
+
+        if (mComposerClient != NULL) {
+            mComposerClient->dispose();
+        }
+        if (mEglContext != EGL_NO_CONTEXT) {
+            eglDestroyContext(mEglDisplay, mEglContext);
+        }
+        if (mEglSurface != EGL_NO_SURFACE) {
+            eglDestroySurface(mEglDisplay, mEglSurface);
+        }
+        if (mEglDisplay != EGL_NO_DISPLAY) {
+            eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
+                    EGL_NO_CONTEXT);
+            eglTerminate(mEglDisplay);
+        }
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    }
+
+    virtual EGLint const* getConfigAttribs() {
+        LOGV("GLTest getConfigAttribs");
+        static EGLint sDefaultConfigAttribs[] = {
+            EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+            EGL_RED_SIZE, 8,
+            EGL_GREEN_SIZE, 8,
+            EGL_BLUE_SIZE, 8,
+            EGL_ALPHA_SIZE, 8,
+            EGL_DEPTH_SIZE, 16,
+            EGL_STENCIL_SIZE, 8,
+            EGL_NONE };
+
+        return sDefaultConfigAttribs;
+    }
+
+    virtual EGLint const* getContextAttribs() {
+        static EGLint sDefaultContextAttribs[] = {
+            EGL_CONTEXT_CLIENT_VERSION, 2,
+            EGL_NONE };
+
+        return sDefaultContextAttribs;
+    }
+
+    virtual EGLint getSurfaceWidth() {
+        return 512;
+    }
+
+    virtual EGLint getSurfaceHeight() {
+        return 512;
+    }
+
+    void loadShader(GLenum shaderType, const char* pSource, GLuint* outShader) {
+        GLuint shader = glCreateShader(shaderType);
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+        if (shader) {
+            glShaderSource(shader, 1, &pSource, NULL);
+            ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+            glCompileShader(shader);
+            ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+            GLint compiled = 0;
+            glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+            ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+            if (!compiled) {
+                GLint infoLen = 0;
+                glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+                ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+                if (infoLen) {
+                    char* buf = (char*) malloc(infoLen);
+                    if (buf) {
+                        glGetShaderInfoLog(shader, infoLen, NULL, buf);
+                        printf("Shader compile log:\n%s\n", buf);
+                        free(buf);
+                        FAIL();
+                    }
+                } else {
+                    char* buf = (char*) malloc(0x1000);
+                    if (buf) {
+                        glGetShaderInfoLog(shader, 0x1000, NULL, buf);
+                        printf("Shader compile log:\n%s\n", buf);
+                        free(buf);
+                        FAIL();
+                    }
+                }
+                glDeleteShader(shader);
+                shader = 0;
+            }
+        }
+        ASSERT_TRUE(shader != 0);
+        *outShader = shader;
+    }
+
+    void createProgram(const char* pVertexSource, const char* pFragmentSource,
+            GLuint* outPgm) {
+        GLuint vertexShader, fragmentShader;
+        {
+            SCOPED_TRACE("compiling vertex shader");
+            loadShader(GL_VERTEX_SHADER, pVertexSource, &vertexShader);
+            if (HasFatalFailure()) {
+                return;
+            }
+        }
+        {
+            SCOPED_TRACE("compiling fragment shader");
+            loadShader(GL_FRAGMENT_SHADER, pFragmentSource, &fragmentShader);
+            if (HasFatalFailure()) {
+                return;
+            }
+        }
+
+        GLuint program = glCreateProgram();
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+        if (program) {
+            glAttachShader(program, vertexShader);
+            ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+            glAttachShader(program, fragmentShader);
+            ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+            glLinkProgram(program);
+            GLint linkStatus = GL_FALSE;
+            glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+            if (linkStatus != GL_TRUE) {
+                GLint bufLength = 0;
+                glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
+                if (bufLength) {
+                    char* buf = (char*) malloc(bufLength);
+                    if (buf) {
+                        glGetProgramInfoLog(program, bufLength, NULL, buf);
+                        printf("Program link log:\n%s\n", buf);
+                        free(buf);
+                        FAIL();
+                    }
+                }
+                glDeleteProgram(program);
+                program = 0;
+            }
+        }
+        glDeleteShader(vertexShader);
+        glDeleteShader(fragmentShader);
+        ASSERT_TRUE(program != 0);
+        *outPgm = program;
+    }
+
+    static int abs(int value) {
+        return value > 0 ? value : -value;
+    }
+
+    ::testing::AssertionResult checkPixel(int x, int y, int r,
+            int g, int b, int a, int tolerance=2) {
+        GLubyte pixel[4];
+        String8 msg;
+        glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+        GLenum err = glGetError();
+        if (err != GL_NO_ERROR) {
+            msg += String8::format("error reading pixel: %#x", err);
+            while ((err = glGetError()) != GL_NO_ERROR) {
+                msg += String8::format(", %#x", err);
+            }
+            fprintf(stderr, "pixel check failure: %s\n", msg.string());
+            return ::testing::AssertionFailure(
+                    ::testing::Message(msg.string()));
+        }
+        if (r >= 0 && abs(r - int(pixel[0])) > tolerance) {
+            msg += String8::format("r(%d isn't %d)", pixel[0], r);
+        }
+        if (g >= 0 && abs(g - int(pixel[1])) > tolerance) {
+            if (!msg.isEmpty()) {
+                msg += " ";
+            }
+            msg += String8::format("g(%d isn't %d)", pixel[1], g);
+        }
+        if (b >= 0 && abs(b - int(pixel[2])) > tolerance) {
+            if (!msg.isEmpty()) {
+                msg += " ";
+            }
+            msg += String8::format("b(%d isn't %d)", pixel[2], b);
+        }
+        if (a >= 0 && abs(a - int(pixel[3])) > tolerance) {
+            if (!msg.isEmpty()) {
+                msg += " ";
+            }
+            msg += String8::format("a(%d isn't %d)", pixel[3], a);
+        }
+        if (!msg.isEmpty()) {
+            fprintf(stderr, "pixel check failure: %s\n", msg.string());
+            return ::testing::AssertionFailure(
+                    ::testing::Message(msg.string()));
+        } else {
+            return ::testing::AssertionSuccess();
+        }
+    }
+
+    int mDisplaySecs;
+    sp<SurfaceComposerClient> mComposerClient;
+    sp<SurfaceControl> mSurfaceControl;
+
+    EGLDisplay mEglDisplay;
+    EGLSurface mEglSurface;
+    EGLContext mEglContext;
+    EGLConfig  mGlConfig;
+};
+
+///////////////////////////////////////////////////////////////////////
+//    Class for  the NON-GL tests
+///////////////////////////////////////////////////////////////////////
 class SurfaceMediaSourceTest : public ::testing::Test {
 public:
 
-    SurfaceMediaSourceTest( ): mYuvTexWidth(64), mYuvTexHeight(66) { }
-    sp<MPEG4Writer>  setUpWriter(OMXClient &client );
+    SurfaceMediaSourceTest( ): mYuvTexWidth(176), mYuvTexHeight(144) { }
     void oneBufferPass(int width, int height );
+    void oneBufferPassNoFill(int width, int height );
     static void fillYV12Buffer(uint8_t* buf, int w, int h, int stride) ;
     static void fillYV12BufferRect(uint8_t* buf, int w, int h,
                         int stride, const android_native_rect_t& rect) ;
@@ -62,10 +363,8 @@
         mSMS->setSynchronousMode(true);
         mSTC = new SurfaceTextureClient(mSMS);
         mANW = mSTC;
-
     }
 
-
     virtual void TearDown() {
         mSMS.clear();
         mSTC.clear();
@@ -78,11 +377,142 @@
     sp<SurfaceMediaSource> mSMS;
     sp<SurfaceTextureClient> mSTC;
     sp<ANativeWindow> mANW;
-
 };
 
+///////////////////////////////////////////////////////////////////////
+//    Class for  the GL tests
+///////////////////////////////////////////////////////////////////////
+class SurfaceMediaSourceGLTest : public GLTest {
+public:
+
+    SurfaceMediaSourceGLTest( ): mYuvTexWidth(176), mYuvTexHeight(144) { }
+    virtual EGLint const* getConfigAttribs();
+    void oneBufferPassGL(int num = 0);
+    static sp<MediaRecorder> setUpMediaRecorder(int fileDescriptor, int videoSource,
+        int outputFormat, int videoEncoder, int width, int height, int fps);
+protected:
+
+    virtual void SetUp() {
+        LOGV("SMS-GLTest::SetUp()");
+        android::ProcessState::self()->startThreadPool();
+        mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight);
+        mSTC = new SurfaceTextureClient(mSMS);
+        mANW = mSTC;
+
+        // Doing the setup related to the GL Side
+        GLTest::SetUp();
+    }
+
+    virtual void TearDown() {
+        mSMS.clear();
+        mSTC.clear();
+        mANW.clear();
+        GLTest::TearDown();
+        eglDestroySurface(mEglDisplay, mSmsEglSurface);
+    }
+
+    void setUpEGLSurfaceFromMediaRecorder(sp<MediaRecorder>& mr);
+
+    const int mYuvTexWidth;
+    const int mYuvTexHeight;
+
+    sp<SurfaceMediaSource> mSMS;
+    sp<SurfaceTextureClient> mSTC;
+    sp<ANativeWindow> mANW;
+    EGLConfig  mSMSGlConfig;
+    EGLSurface  mSmsEglSurface;
+};
+
+/////////////////////////////////////////////////////////////////////
+// Methods in SurfaceMediaSourceGLTest
+/////////////////////////////////////////////////////////////////////
+EGLint const* SurfaceMediaSourceGLTest::getConfigAttribs() {
+        LOGV("SurfaceMediaSourceGLTest getConfigAttribs");
+    static EGLint sDefaultConfigAttribs[] = {
+        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+        EGL_RED_SIZE, 8,
+        EGL_GREEN_SIZE, 8,
+        EGL_BLUE_SIZE, 8,
+        EGL_RECORDABLE_ANDROID, EGL_TRUE,
+        EGL_NONE };
+
+    return sDefaultConfigAttribs;
+}
+
+// One pass of dequeuing and queuing a GLBuffer
+void SurfaceMediaSourceGLTest::oneBufferPassGL(int num) {
+    int d = num % 50;
+    float f = 0.2f; // 0.1f * d;
+
+    glClearColor(0, 0.3, 0, 0.6);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    glEnable(GL_SCISSOR_TEST);
+    glScissor(4 + d, 4 + d, 4, 4);
+    glClearColor(1.0 - f, f, f, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    glScissor(24 + d, 48 + d, 4, 4);
+    glClearColor(f, 1.0 - f, f, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    glScissor(37 + d, 17 + d, 4, 4);
+    glClearColor(f, f, 1.0 - f, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    // The following call dequeues and queues the buffer
+    eglSwapBuffers(mEglDisplay, mSmsEglSurface);
+    glDisable(GL_SCISSOR_TEST);
+}
+
+// Set up the MediaRecorder which runs in the same process as mediaserver
+sp<MediaRecorder> SurfaceMediaSourceGLTest::setUpMediaRecorder(int fd, int videoSource,
+        int outputFormat, int videoEncoder, int width, int height, int fps) {
+    sp<MediaRecorder> mr = new MediaRecorder();
+    mr->setVideoSource(videoSource);
+    mr->setOutputFormat(outputFormat);
+    mr->setVideoEncoder(videoEncoder);
+    mr->setOutputFile(fd, 0, 0);
+    mr->setVideoSize(width, height);
+    mr->setVideoFrameRate(fps);
+    mr->prepare();
+    LOGV("Starting MediaRecorder...");
+    CHECK_EQ(OK, mr->start());
+    return mr;
+}
+
+// query the mediarecorder for a surfacemeidasource and create an egl surface with that
+void SurfaceMediaSourceGLTest::setUpEGLSurfaceFromMediaRecorder(sp<MediaRecorder>& mr) {
+    sp<ISurfaceTexture> iST = mr->querySurfaceMediaSourceFromMediaServer();
+    mSTC = new SurfaceTextureClient(iST);
+    mANW = mSTC;
+
+    EGLint numConfigs = 0;
+    EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mSMSGlConfig,
+            1, &numConfigs));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    LOGV("Native Window = %p, mSTC = %p", mANW.get(), mSTC.get());
+
+    mSmsEglSurface = eglCreateWindowSurface(mEglDisplay, mSMSGlConfig,
+                                mANW.get(), NULL);
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    ASSERT_NE(EGL_NO_SURFACE, mSmsEglSurface) ;
+
+    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mSmsEglSurface, mSmsEglSurface,
+            mEglContext));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+}
+
+
+/////////////////////////////////////////////////////////////////////
+// Methods in SurfaceMediaSourceTest
+/////////////////////////////////////////////////////////////////////
+
+// One pass of dequeuing and queuing the buffer. Fill it in with
+// cpu YV12 buffer
 void SurfaceMediaSourceTest::oneBufferPass(int width, int height ) {
-    LOGV("One Buffer Pass");
     ANativeWindowBuffer* anb;
     ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
     ASSERT_TRUE(anb != NULL);
@@ -99,42 +529,16 @@
     ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer()));
 }
 
-sp<MPEG4Writer> SurfaceMediaSourceTest::setUpWriter(OMXClient &client ) {
-    // Writing to a file
-    const char *fileName = "/sdcard/outputSurfEnc.mp4";
-    sp<MetaData> enc_meta = new MetaData;
-    enc_meta->setInt32(kKeyBitRate, 300000);
-    enc_meta->setInt32(kKeyFrameRate, 30);
+// Dequeuing and queuing the buffer without really filling it in.
+void SurfaceMediaSourceTest::oneBufferPassNoFill(int width, int height ) {
+    ANativeWindowBuffer* anb;
+    ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
+    ASSERT_TRUE(anb != NULL);
 
-    enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
-
-    sp<MetaData> meta = mSMS->getFormat();
-
-    int32_t width, height, stride, sliceHeight, colorFormat;
-    CHECK(meta->findInt32(kKeyWidth, &width));
-    CHECK(meta->findInt32(kKeyHeight, &height));
-    CHECK(meta->findInt32(kKeyStride, &stride));
-    CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight));
-    CHECK(meta->findInt32(kKeyColorFormat, &colorFormat));
-
-    enc_meta->setInt32(kKeyWidth, width);
-    enc_meta->setInt32(kKeyHeight, height);
-    enc_meta->setInt32(kKeyIFramesInterval, 1);
-    enc_meta->setInt32(kKeyStride, stride);
-    enc_meta->setInt32(kKeySliceHeight, sliceHeight);
-    // TODO: overwriting the colorformat since the format set by GRAlloc
-    // could be wrong or not be read by OMX
-    enc_meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420Planar);
-
-
-    sp<MediaSource> encoder =
-        OMXCodec::Create(
-                client.interface(), enc_meta, true /* createEncoder */, mSMS);
-
-    sp<MPEG4Writer> writer = new MPEG4Writer(fileName);
-    writer->addSource(encoder);
-
-    return writer;
+    sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+    // ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer()));
+    // We do not fill the buffer in. Just queue it back.
+    ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer()));
 }
 
 // Fill a YV12 buffer with a multi-colored checkerboard pattern
@@ -216,46 +620,53 @@
             return OK;
         }
 };
-
 ///////////////////////////////////////////////////////////////////
 //           TESTS
+// SurfaceMediaSourceTest class contains tests that fill the buffers
+// using the cpu calls
+// SurfaceMediaSourceGLTest class contains tests that fill the buffers
+// using the GL calls.
+// TODO: None of the tests actually verify the encoded images.. so at this point,
+// these are mostly functionality tests + visual inspection
+//////////////////////////////////////////////////////////////////////
+
 // Just pass one buffer from the native_window to the SurfaceMediaSource
-TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotOneBufferPass) {
+// Dummy Encoder
+static int testId = 1;
+TEST_F(SurfaceMediaSourceTest, DISABLED_DummyEncodingFromCpuFilledYV12BufferNpotOneBufferPass) {
+    LOGV("Test # %d", testId++);
     LOGV("Testing OneBufferPass ******************************");
 
-    ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
-            0, 0, HAL_PIXEL_FORMAT_YV12));
-    ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
-            GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
-
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
+            HAL_PIXEL_FORMAT_YV12));
     oneBufferPass(mYuvTexWidth, mYuvTexHeight);
 }
 
 // Pass the buffer with the wrong height and weight and should not be accepted
-TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotWrongSizeBufferPass) {
+// Dummy Encoder
+TEST_F(SurfaceMediaSourceTest, DISABLED_DummyEncodingFromCpuFilledYV12BufferNpotWrongSizeBufferPass) {
+    LOGV("Test # %d", testId++);
     LOGV("Testing Wrong size BufferPass ******************************");
 
     // setting the client side buffer size different than the server size
-    ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
-             10, 10, HAL_PIXEL_FORMAT_YV12));
-    ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
-            GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(),
+             10, 10));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
+            HAL_PIXEL_FORMAT_YV12));
 
     ANativeWindowBuffer* anb;
 
-    // make sure we get an error back when dequeuing!
+    // Note: make sure we get an ERROR back when dequeuing!
     ASSERT_NE(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
 }
 
-
 // pass multiple buffers from the native_window the SurfaceMediaSource
-// A dummy writer is used to simulate actual MPEG4Writer
-TEST_F(SurfaceMediaSourceTest,  EncodingFromCpuFilledYV12BufferNpotMultiBufferPass) {
+// Dummy Encoder
+TEST_F(SurfaceMediaSourceTest,  DISABLED_DummyEncodingFromCpuFilledYV12BufferNpotMultiBufferPass) {
+    LOGV("Test # %d", testId++);
     LOGV("Testing MultiBufferPass, Dummy Recorder *********************");
-    ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
-            0, 0, HAL_PIXEL_FORMAT_YV12));
-    ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
-            GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
+            HAL_PIXEL_FORMAT_YV12));
 
     SimpleDummyRecorder writer(mSMS);
     writer.start();
@@ -272,14 +683,13 @@
 }
 
 // Delayed pass of multiple buffers from the native_window the SurfaceMediaSource
-// A dummy writer is used to simulate actual MPEG4Writer
-TEST_F(SurfaceMediaSourceTest,  EncodingFromCpuFilledYV12BufferNpotMultiBufferPassLag) {
+// Dummy Encoder
+TEST_F(SurfaceMediaSourceTest,  DISABLED_DummyLagEncodingFromCpuFilledYV12BufferNpotMultiBufferPass) {
+    LOGV("Test # %d", testId++);
     LOGV("Testing MultiBufferPass, Dummy Recorder Lagging **************");
 
-    ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
-            0, 0, HAL_PIXEL_FORMAT_YV12));
-    ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
-            GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
+            HAL_PIXEL_FORMAT_YV12));
 
     SimpleDummyRecorder writer(mSMS);
     writer.start();
@@ -299,12 +709,11 @@
 
 // pass multiple buffers from the native_window the SurfaceMediaSource
 // A dummy writer (MULTITHREADED) is used to simulate actual MPEG4Writer
-TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPassThreaded) {
+TEST_F(SurfaceMediaSourceTest, DISABLED_DummyThreadedEncodingFromCpuFilledYV12BufferNpotMultiBufferPass) {
+    LOGV("Test # %d", testId++);
     LOGV("Testing MultiBufferPass, Dummy Recorder Multi-Threaded **********");
-    ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
-            0, 0, HAL_PIXEL_FORMAT_YV12));
-    ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
-            GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
+            HAL_PIXEL_FORMAT_YV12));
 
     DummyRecorder writer(mSMS);
     writer.start();
@@ -318,32 +727,210 @@
     writer.stop();
 }
 
-// Test to examine the actual encoding. Temporarily disabled till the
-// colorformat and encoding from GRAlloc data is resolved
-TEST_F(SurfaceMediaSourceTest, DISABLED_EncodingFromCpuFilledYV12BufferNpotWrite) {
-    LOGV("Testing the whole pipeline with actual Recorder");
-    ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
-            0, 0, HAL_PIXEL_FORMAT_YV12));
-    ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
-            GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
-    OMXClient client;
-    CHECK_EQ(OK, client.connect());
+// Test to examine actual encoding using mediarecorder
+// We use the mediaserver to create a mediarecorder and send
+// it back to us. So SurfaceMediaSource lives in the same process
+// as the mediaserver.
+// Very close to the actual camera, except that the
+// buffers are filled and queueud by the CPU instead of GL.
+TEST_F(SurfaceMediaSourceTest, DISABLED_EncodingFromCpuYV12BufferNpotWriteMediaServer) {
+    LOGV("Test # %d", testId++);
+    LOGV("************** Testing the whole pipeline with actual MediaRecorder ***********");
+    LOGV("************** SurfaceMediaSource is same process as mediaserver    ***********");
 
-    sp<MPEG4Writer> writer = setUpWriter(client);
-    int64_t start = systemTime();
-    CHECK_EQ(OK, writer->start());
+    const char *fileName = "/sdcard/outputSurfEncMSource.mp4";
+    int fd = open(fileName, O_RDWR | O_CREAT, 0744);
+    if (fd < 0) {
+        LOGE("ERROR: Could not open the the file %s, fd = %d !!", fileName, fd);
+    }
+    CHECK(fd >= 0);
+
+    sp<MediaRecorder> mr = SurfaceMediaSourceGLTest::setUpMediaRecorder(fd,
+            VIDEO_SOURCE_GRALLOC_BUFFER,
+            OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264, mYuvTexWidth,
+            mYuvTexHeight, 30);
+    // get the reference to the surfacemediasource living in
+    // mediaserver that is created by stagefrightrecorder
+    sp<ISurfaceTexture> iST = mr->querySurfaceMediaSourceFromMediaServer();
+    mSTC = new SurfaceTextureClient(iST);
+    mANW = mSTC;
+    ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
+                                                HAL_PIXEL_FORMAT_YV12));
 
     int32_t nFramesCount = 0;
     while (nFramesCount <= 300) {
-        oneBufferPass(mYuvTexWidth, mYuvTexHeight);
+        oneBufferPassNoFill(mYuvTexWidth, mYuvTexHeight);
         nFramesCount++;
+        LOGV("framesCount = %d", nFramesCount);
     }
 
-    CHECK_EQ(OK, writer->stop());
-    writer.clear();
-    int64_t end = systemTime();
-    client.disconnect();
+    ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU));
+    LOGV("Stopping MediaRecorder...");
+    CHECK_EQ(OK, mr->stop());
+    mr.clear();
+    close(fd);
 }
 
+//////////////////////////////////////////////////////////////////////
+// GL tests
+/////////////////////////////////////////////////////////////////////
 
+// Test to examine whether we can choose the Recordable Android GLConfig
+// DummyRecorder used- no real encoding here
+TEST_F(SurfaceMediaSourceGLTest, ChooseAndroidRecordableEGLConfigDummyWrite) {
+    LOGV("Test # %d", testId++);
+    LOGV("Test to verify creating a surface w/ right config *********");
+
+    mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight);
+    mSTC = new SurfaceTextureClient(mSMS);
+    mANW = mSTC;
+
+    DummyRecorder writer(mSMS);
+    writer.start();
+
+    EGLint numConfigs = 0;
+    EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mSMSGlConfig,
+            1, &numConfigs));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    mSmsEglSurface = eglCreateWindowSurface(mEglDisplay, mSMSGlConfig,
+                                mANW.get(), NULL);
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    ASSERT_NE(EGL_NO_SURFACE, mSmsEglSurface) ;
+
+    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mSmsEglSurface, mSmsEglSurface,
+            mEglContext));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    int32_t nFramesCount = 0;
+    while (nFramesCount <= 300) {
+        oneBufferPassGL();
+        nFramesCount++;
+        LOGV("framesCount = %d", nFramesCount);
+    }
+
+    ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_EGL));
+    writer.stop();
+}
+// Test to examine whether we can render GL buffers in to the surface
+// created with the native window handle
+TEST_F(SurfaceMediaSourceGLTest, RenderingToRecordableEGLSurfaceWorks) {
+    LOGV("Test # %d", testId++);
+    LOGV("RenderingToRecordableEGLSurfaceWorks *********************");
+    // Do the producer side of things
+    glClearColor(0.6, 0.6, 0.6, 0.6);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    glEnable(GL_SCISSOR_TEST);
+    glScissor(4, 4, 4, 4);
+    glClearColor(1.0, 0.0, 0.0, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    glScissor(24, 48, 4, 4);
+    glClearColor(0.0, 1.0, 0.0, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    glScissor(37, 17, 4, 4);
+    glClearColor(0.0, 0.0, 1.0, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    EXPECT_TRUE(checkPixel( 0,  0, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(63,  0, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(63, 63, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel( 0, 63, 153, 153, 153, 153));
+
+    EXPECT_TRUE(checkPixel( 4,  7, 255,   0,   0, 255));
+    EXPECT_TRUE(checkPixel(25, 51,   0, 255,   0, 255));
+    EXPECT_TRUE(checkPixel(40, 19,   0,   0, 255, 255));
+    EXPECT_TRUE(checkPixel(29, 51, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel( 5, 32, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(13,  8, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(46,  3, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(30, 33, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel( 6, 52, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(55, 33, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(16, 29, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel( 1, 30, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(41, 37, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(46, 29, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(15, 25, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153));
+}
+
+// Test to examine the actual encoding with GL buffers
+// Actual encoder, Actual GL Buffers Filled SurfaceMediaSource
+// The same pattern is rendered every frame
+TEST_F(SurfaceMediaSourceGLTest, EncodingFromGLRgbaSameImageEachBufNpotWrite) {
+    LOGV("Test # %d", testId++);
+    LOGV("************** Testing the whole pipeline with actual Recorder ***********");
+    LOGV("************** GL Filling the buffers ***********");
+    // Note: No need to set the colorformat for the buffers. The colorformat is
+    // in the GRAlloc buffers itself.
+
+    const char *fileName = "/sdcard/outputSurfEncMSourceGL.mp4";
+    int fd = open(fileName, O_RDWR | O_CREAT, 0744);
+    if (fd < 0) {
+        LOGE("ERROR: Could not open the the file %s, fd = %d !!", fileName, fd);
+    }
+    CHECK(fd >= 0);
+
+    sp<MediaRecorder> mr = setUpMediaRecorder(fd, VIDEO_SOURCE_GRALLOC_BUFFER,
+            OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264, mYuvTexWidth, mYuvTexHeight, 30);
+
+    // get the reference to the surfacemediasource living in
+    // mediaserver that is created by stagefrightrecorder
+    setUpEGLSurfaceFromMediaRecorder(mr);
+
+    int32_t nFramesCount = 0;
+    while (nFramesCount <= 300) {
+        oneBufferPassGL();
+        nFramesCount++;
+        LOGV("framesCount = %d", nFramesCount);
+    }
+
+    ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_EGL));
+    LOGV("Stopping MediaRecorder...");
+    CHECK_EQ(OK, mr->stop());
+    mr.clear();
+    close(fd);
+}
+
+// Test to examine the actual encoding from the GL Buffers
+// Actual encoder, Actual GL Buffers Filled SurfaceMediaSource
+// A different pattern is rendered every frame
+TEST_F(SurfaceMediaSourceGLTest, EncodingFromGLRgbaDiffImageEachBufNpotWrite) {
+    LOGV("Test # %d", testId++);
+    LOGV("************** Testing the whole pipeline with actual Recorder ***********");
+    LOGV("************** Diff GL Filling the buffers ***********");
+    // Note: No need to set the colorformat for the buffers. The colorformat is
+    // in the GRAlloc buffers itself.
+
+    const char *fileName = "/sdcard/outputSurfEncMSourceGLDiff.mp4";
+    int fd = open(fileName, O_RDWR | O_CREAT, 0744);
+    if (fd < 0) {
+        LOGE("ERROR: Could not open the the file %s, fd = %d !!", fileName, fd);
+    }
+    CHECK(fd >= 0);
+
+    sp<MediaRecorder> mr = setUpMediaRecorder(fd, VIDEO_SOURCE_GRALLOC_BUFFER,
+            OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264, mYuvTexWidth, mYuvTexHeight, 30);
+
+    // get the reference to the surfacemediasource living in
+    // mediaserver that is created by stagefrightrecorder
+    setUpEGLSurfaceFromMediaRecorder(mr);
+
+    int32_t nFramesCount = 0;
+    while (nFramesCount <= 300) {
+        oneBufferPassGL(nFramesCount);
+        nFramesCount++;
+        LOGV("framesCount = %d", nFramesCount);
+    }
+
+    ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_EGL));
+    LOGV("Stopping MediaRecorder...");
+    CHECK_EQ(OK, mr->stop());
+    mr.clear();
+    close(fd);
+}
 } // namespace android
diff --git a/opengl/java/android/opengl/GLUtils.java b/opengl/java/android/opengl/GLUtils.java
index f30a4cd4..125c56e 100644
--- a/opengl/java/android/opengl/GLUtils.java
+++ b/opengl/java/android/opengl/GLUtils.java
@@ -16,9 +16,11 @@
 
 package android.opengl;
 
-import javax.microedition.khronos.opengles.GL10;
 import android.graphics.Bitmap;
 
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGL11;
+
 /**
  *
  * Utility class to help bridging OpenGL ES and Android APIs.
@@ -222,6 +224,51 @@
         }
     }
 
+    /**
+     * Return a string for the EGL error code, or the hex representation
+     * if the error is unknown.
+     * 
+     * @param error The EGL error to convert into a String.
+     * 
+     * @return An error string corresponding to the EGL error code.
+     */
+    public static String getEGLErrorString(int error) {
+        switch (error) {
+            case EGL10.EGL_SUCCESS:
+                return "EGL_SUCCESS";
+            case EGL10.EGL_NOT_INITIALIZED:
+                return "EGL_NOT_INITIALIZED";
+            case EGL10.EGL_BAD_ACCESS:
+                return "EGL_BAD_ACCESS";
+            case EGL10.EGL_BAD_ALLOC:
+                return "EGL_BAD_ALLOC";
+            case EGL10.EGL_BAD_ATTRIBUTE:
+                return "EGL_BAD_ATTRIBUTE";
+            case EGL10.EGL_BAD_CONFIG:
+                return "EGL_BAD_CONFIG";
+            case EGL10.EGL_BAD_CONTEXT:
+                return "EGL_BAD_CONTEXT";
+            case EGL10.EGL_BAD_CURRENT_SURFACE:
+                return "EGL_BAD_CURRENT_SURFACE";
+            case EGL10.EGL_BAD_DISPLAY:
+                return "EGL_BAD_DISPLAY";
+            case EGL10.EGL_BAD_MATCH:
+                return "EGL_BAD_MATCH";
+            case EGL10.EGL_BAD_NATIVE_PIXMAP:
+                return "EGL_BAD_NATIVE_PIXMAP";
+            case EGL10.EGL_BAD_NATIVE_WINDOW:
+                return "EGL_BAD_NATIVE_WINDOW";
+            case EGL10.EGL_BAD_PARAMETER:
+                return "EGL_BAD_PARAMETER";
+            case EGL10.EGL_BAD_SURFACE:
+                return "EGL_BAD_SURFACE";
+            case EGL11.EGL_CONTEXT_LOST:
+                return "EGL_CONTEXT_LOST";
+            default:
+                return "0x" + Integer.toHexString(error);
+        }
+    }
+
     native private static void nativeClassInit();
 
     native private static int native_getInternalFormat(Bitmap bitmap);
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index e1231a5..5a3850d 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -16,41 +16,65 @@
 
 package com.android.systemui;
 
-import java.io.IOException;
-
+import android.app.ActivityManager;
 import android.app.WallpaperManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.Region.Op;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
+import android.opengl.GLUtils;
+import android.renderscript.Matrix4f;
 import android.service.wallpaper.WallpaperService;
 import android.util.Log;
-import android.util.Slog;
+import android.view.Display;
 import android.view.MotionEvent;
 import android.view.SurfaceHolder;
-import android.content.Context;
-import android.content.IntentFilter;
-import android.content.Intent;
-import android.content.BroadcastReceiver;
+import android.view.WindowManager;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+import javax.microedition.khronos.opengles.GL;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import static android.opengl.GLES20.*;
+import static javax.microedition.khronos.egl.EGL10.*;
 
 /**
  * Default built-in wallpaper that simply shows a static image.
  */
+@SuppressWarnings({"UnusedDeclaration"})
 public class ImageWallpaper extends WallpaperService {
     private static final String TAG = "ImageWallpaper";
+    private static final String GL_LOG_TAG = "ImageWallpaperGL";
     private static final boolean DEBUG = false;
 
     static final boolean FIXED_SIZED_SURFACE = true;
+    static final boolean USE_OPENGL = false;
 
     WallpaperManager mWallpaperManager;
-    private Handler mHandler;
+
+    boolean mIsHwAccelerated;
 
     @Override
     public void onCreate() {
         super.onCreate();
         mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE);
-        mHandler = new Handler();
+
+        //noinspection PointlessBooleanExpression,ConstantConditions
+        if (FIXED_SIZED_SURFACE && USE_OPENGL) {
+            WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
+            Display display = windowManager.getDefaultDisplay();
+            mIsHwAccelerated = ActivityManager.isHighEndGfx(display);
+        }
     }
 
     public Engine onCreateEngine() {
@@ -58,9 +82,16 @@
     }
 
     class DrawableEngine extends Engine {
-        private final Object mLock = new Object();
+        static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+        static final int EGL_OPENGL_ES2_BIT = 4;
+
+        private final Object mLock = new Object[0];
+
+        // TODO: Not currently used, keeping around until we know we don't need it
+        @SuppressWarnings({"UnusedDeclaration"})
         private WallpaperObserver mReceiver;
-        Drawable mBackground;
+
+        Bitmap mBackground;
         int mBackgroundWidth = -1, mBackgroundHeight = -1;
         float mXOffset;
         float mYOffset;
@@ -71,6 +102,35 @@
         int mLastXTranslation;
         int mLastYTranslation;
 
+        private EGL10 mEgl;
+        private EGLDisplay mEglDisplay;
+        private EGLConfig mEglConfig;
+        private EGLContext mEglContext;
+        private EGLSurface mEglSurface;
+        private GL mGL;
+
+        private static final String sSimpleVS =
+                "attribute vec4 position;\n" +
+                "attribute vec2 texCoords;\n" +
+                "varying vec2 outTexCoords;\n" +
+                "uniform mat4 projection;\n" +
+                "\nvoid main(void) {\n" +
+                "    outTexCoords = texCoords;\n" +
+                "    gl_Position = projection * position;\n" +
+                "}\n\n";
+        private static final String sSimpleFS =
+                "precision mediump float;\n\n" +
+                "varying vec2 outTexCoords;\n" +
+                "uniform sampler2D texture;\n" +
+                "\nvoid main(void) {\n" +
+                "    gl_FragColor = texture2D(texture, outTexCoords);\n" +
+                "}\n\n";
+    
+        private static final int FLOAT_SIZE_BYTES = 4;
+        private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
+        private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
+        private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
+
         class WallpaperObserver extends BroadcastReceiver {
             public void onReceive(Context context, Intent intent) {
                 if (DEBUG) {
@@ -94,7 +154,7 @@
 
             super.onCreate(surfaceHolder);
             
-            // Don't need this currently because the wallpaper service
+            // TODO: Don't need this currently because the wallpaper service
             // will restart the image wallpaper whenever the image changes.
             //IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
             //mReceiver = new WallpaperObserver();
@@ -221,8 +281,7 @@
             int yPixels = availh < 0 ? (int)(availh * mYOffset + .5f) : (availh / 2);
 
             mOffsetsChanged = false;
-            if (!mRedrawNeeded
-                    && xPixels == mLastXTranslation && yPixels == mLastYTranslation) {
+            if (!mRedrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) {
                 if (DEBUG) {
                     Log.d(TAG, "Suppressed drawFrame since the image has not "
                             + "actually moved an integral number of pixels.");
@@ -240,27 +299,10 @@
                 updateWallpaperLocked();
             }
 
-            //Slog.i(TAG, "************** DRAWING WALLAPER ******************");
-            Canvas c = sh.lockCanvas();
-            if (c != null) {
-                try {
-                    if (DEBUG) {
-                        Log.d(TAG, "Redrawing: xPixels=" + xPixels + ", yPixels=" + yPixels);
-                    }
-
-                    c.translate(xPixels, yPixels);
-                    if (availw < 0 || availh < 0) {
-                        c.save(Canvas.CLIP_SAVE_FLAG);
-                        c.clipRect(0, 0, mBackgroundWidth, mBackgroundHeight, Op.DIFFERENCE);
-                        c.drawColor(0xff000000);
-                        c.restore();
-                    }
-                    if (mBackground != null) {
-                        mBackground.draw(c);
-                    }
-                } finally {
-                    sh.unlockCanvasAndPost(c);
-                }
+            if (mIsHwAccelerated) {
+                drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels);
+            } else {
+                drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
             }
 
             if (FIXED_SIZED_SURFACE) {
@@ -274,15 +316,15 @@
         }
 
         void updateWallpaperLocked() {
-            //Slog.i(TAG, "************** LOADING WALLAPER ******************");
             Throwable exception = null;
             try {
-                mBackground = mWallpaperManager.getFastDrawable();
+                mBackground = mWallpaperManager.getBitmap();
             } catch (RuntimeException e) {
                 exception = e;
             } catch (OutOfMemoryError e) {
                 exception = e;
             }
+
             if (exception != null) {
                 mBackground = null;
                 // Note that if we do fail at this, and the default wallpaper can't
@@ -296,8 +338,277 @@
                     Log.w(TAG, "Unable reset to default wallpaper!", ex);
                 }
             }
-            mBackgroundWidth = mBackground != null ? mBackground.getIntrinsicWidth() : 0;
-            mBackgroundHeight = mBackground != null ? mBackground.getIntrinsicHeight() : 0;
+
+            mBackgroundWidth = mBackground != null ? mBackground.getWidth() : 0;
+            mBackgroundHeight = mBackground != null ? mBackground.getHeight() : 0;
+        }
+
+        private void drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int x, int y) {
+            Canvas c = sh.lockCanvas();
+            if (c != null) {
+                try {
+                    if (DEBUG) {
+                        Log.d(TAG, "Redrawing: x=" + x + ", y=" + y);
+                    }
+
+                    c.translate(x, y);
+                    if (w < 0 || h < 0) {
+                        c.save(Canvas.CLIP_SAVE_FLAG);
+                        c.clipRect(0, 0, mBackgroundWidth, mBackgroundHeight, Op.DIFFERENCE);
+                        c.drawColor(0xff000000);
+                        c.restore();
+                    }
+                    if (mBackground != null) {
+                        c.drawBitmap(mBackground, 0, 0, null);
+                    }
+                } finally {
+                    sh.unlockCanvasAndPost(c);
+                }
+            }
+        }
+
+        private void drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top) {
+            initGL(sh);
+
+            final float right = left + mBackgroundWidth;
+            final float bottom = top + mBackgroundHeight;
+
+            final Rect frame = sh.getSurfaceFrame();
+
+            final Matrix4f ortho = new Matrix4f();
+            ortho.loadOrtho(0.0f, frame.width(), frame.height(), 0.0f, -1.0f, 1.0f);
+
+            final FloatBuffer triangleVertices = createMesh(left, top, right, bottom);
+
+            final int texture = loadTexture(mBackground);
+            final int program = buildProgram(sSimpleVS, sSimpleFS);
+    
+            final int attribPosition = glGetAttribLocation(program, "position");
+            final int attribTexCoords = glGetAttribLocation(program, "texCoords");
+            final int uniformTexture = glGetUniformLocation(program, "texture");
+            final int uniformProjection = glGetUniformLocation(program, "projection");
+
+            checkGlError();
+
+            glViewport(0, 0, frame.width(), frame.height());
+            glBindTexture(GL_TEXTURE_2D, texture);
+
+            glUseProgram(program);
+            glEnableVertexAttribArray(attribPosition);
+            glEnableVertexAttribArray(attribTexCoords);
+            glUniform1i(uniformTexture, 0);
+            glUniformMatrix4fv(uniformProjection, 1, false, ortho.getArray(), 0);
+
+            checkGlError();
+
+            if (w < 0 || h < 0) {
+                glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+                glClear(GL_COLOR_BUFFER_BIT);
+            }
+    
+            // drawQuad
+            triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
+            glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
+                    TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
+
+            triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
+            glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
+                    TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
+
+            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+    
+            if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
+                throw new RuntimeException("Cannot swap buffers");
+            }
+            checkEglError();
+    
+            finishGL();
+        }
+
+        private FloatBuffer createMesh(int left, int top, float right, float bottom) {
+            final float[] verticesData = {
+                    // X, Y, Z, U, V
+                     left,  bottom, 0.0f, 0.0f, 1.0f,
+                     right, bottom, 0.0f, 1.0f, 1.0f,
+                     left,  top,    0.0f, 0.0f, 0.0f,
+                     right, top,    0.0f, 1.0f, 0.0f,
+            };
+
+            final int bytes = verticesData.length * FLOAT_SIZE_BYTES;
+            final FloatBuffer triangleVertices = ByteBuffer.allocateDirect(bytes).order(
+                    ByteOrder.nativeOrder()).asFloatBuffer();
+            triangleVertices.put(verticesData).position(0);
+            return triangleVertices;
+        }
+
+        private int loadTexture(Bitmap bitmap) {
+            int[] textures = new int[1];
+    
+            glActiveTexture(GL_TEXTURE0);
+            glGenTextures(1, textures, 0);
+            checkGlError();
+    
+            int texture = textures[0];
+            glBindTexture(GL_TEXTURE_2D, texture);
+            checkGlError();
+            
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    
+            GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0);
+            checkGlError();
+
+            bitmap.recycle();
+    
+            return texture;
+        }
+        
+        private int buildProgram(String vertex, String fragment) {
+            int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
+            if (vertexShader == 0) return 0;
+    
+            int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
+            if (fragmentShader == 0) return 0;
+    
+            int program = glCreateProgram();
+            glAttachShader(program, vertexShader);
+            checkGlError();
+    
+            glAttachShader(program, fragmentShader);
+            checkGlError();
+    
+            glLinkProgram(program);
+            checkGlError();
+    
+            int[] status = new int[1];
+            glGetProgramiv(program, GL_LINK_STATUS, status, 0);
+            if (status[0] != GL_TRUE) {
+                String error = glGetProgramInfoLog(program);
+                Log.d(GL_LOG_TAG, "Error while linking program:\n" + error);
+                glDeleteShader(vertexShader);
+                glDeleteShader(fragmentShader);
+                glDeleteProgram(program);
+                return 0;
+            }
+    
+            return program;
+        }
+        
+        private int buildShader(String source, int type) {
+            int shader = glCreateShader(type);
+    
+            glShaderSource(shader, source);
+            checkGlError();
+    
+            glCompileShader(shader);
+            checkGlError();
+    
+            int[] status = new int[1];
+            glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
+            if (status[0] != GL_TRUE) {
+                String error = glGetShaderInfoLog(shader);
+                Log.d(GL_LOG_TAG, "Error while compiling shader:\n" + error);
+                glDeleteShader(shader);
+                return 0;
+            }
+            
+            return shader;
+        }
+    
+        private void checkEglError() {
+            int error = mEgl.eglGetError();
+            if (error != EGL_SUCCESS) {
+                Log.w(GL_LOG_TAG, "EGL error = " + GLUtils.getEGLErrorString(error));
+            }
+        }
+    
+        private void checkGlError() {
+            int error = glGetError();
+            if (error != GL_NO_ERROR) {
+                Log.w(GL_LOG_TAG, "GL error = 0x" + Integer.toHexString(error), new Throwable());
+            }
+        }
+    
+        private void finishGL() {
+            mEgl.eglDestroyContext(mEglDisplay, mEglContext);
+            mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
+        }
+        
+        private void initGL(SurfaceHolder surfaceHolder) {
+            mEgl = (EGL10) EGLContext.getEGL();
+    
+            mEglDisplay = mEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
+            if (mEglDisplay == EGL_NO_DISPLAY) {
+                throw new RuntimeException("eglGetDisplay failed " +
+                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
+            }
+            
+            int[] version = new int[2];
+            if (!mEgl.eglInitialize(mEglDisplay, version)) {
+                throw new RuntimeException("eglInitialize failed " +
+                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
+            }
+    
+            mEglConfig = chooseEglConfig();
+            if (mEglConfig == null) {
+                throw new RuntimeException("eglConfig not initialized");
+            }
+            
+            mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
+    
+            mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null);
+    
+            if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
+                int error = mEgl.eglGetError();
+                if (error == EGL_BAD_NATIVE_WINDOW) {
+                    Log.e(GL_LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
+                    return;
+                }
+                throw new RuntimeException("createWindowSurface failed " +
+                        GLUtils.getEGLErrorString(error));
+            }
+    
+            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+                throw new RuntimeException("eglMakeCurrent failed " +
+                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
+            }
+    
+            mGL = mEglContext.getGL();
+        }
+        
+    
+        EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
+            int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+            return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attrib_list);            
+        }
+    
+        private EGLConfig chooseEglConfig() {
+            int[] configsCount = new int[1];
+            EGLConfig[] configs = new EGLConfig[1];
+            int[] configSpec = getConfig();
+            if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
+                throw new IllegalArgumentException("eglChooseConfig failed " +
+                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
+            } else if (configsCount[0] > 0) {
+                return configs[0];
+            }
+            return null;
+        }
+
+        private int[] getConfig() {
+            return new int[] {
+                    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+                    EGL_RED_SIZE, 8,
+                    EGL_GREEN_SIZE, 8,
+                    EGL_BLUE_SIZE, 8,
+                    EGL_ALPHA_SIZE, 0,
+                    EGL_DEPTH_SIZE, 0,
+                    EGL_STENCIL_SIZE, 0,
+                    EGL_NONE
+            };
         }
     }
 }
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 9765f2a..565063a 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -75,7 +75,7 @@
     static final String TAG = "WallpaperService";
     static final boolean DEBUG = false;
 
-    Object mLock = new Object();
+    final Object mLock = new Object[0];
 
     /**
      * Minimum time between crashes of a wallpaper service for us to consider
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index c935733..7232a94 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -9026,6 +9026,7 @@
     final static class MemItem {
         final String label;
         final long pss;
+        ArrayList<MemItem> subitems;
 
         public MemItem(String _label, long _pss) {
             label = _label;
@@ -9051,7 +9052,10 @@
 
         for (int i=0; i<items.size(); i++) {
             MemItem mi = items.get(i);
-            pw.print(prefix); pw.printf("%8d Kb: ", mi.pss); pw.println(mi.label);
+            pw.print(prefix); pw.printf("%7d Kb: ", mi.pss); pw.println(mi.label);
+            if (mi.subitems != null) {
+                dumpMemItems(pw, prefix + "           ", mi.subitems, true);
+            }
         }
     }
 
@@ -9119,6 +9123,7 @@
                 "Backup", "Services", "Home", "Background"
         };
         long oomPss[] = new long[oomLabel.length];
+        ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[])new ArrayList[oomLabel.length];
 
         long totalPss = 0;
 
@@ -9147,7 +9152,9 @@
                 if (!isCheckinRequest && mi != null) {
                     long myTotalPss = mi.getTotalPss();
                     totalPss += myTotalPss;
-                    procMems.add(new MemItem(r.processName + " (pid " + r.pid + ")", myTotalPss));
+                    MemItem pssItem = new MemItem(r.processName + " (pid " + r.pid + ")",
+                            myTotalPss);
+                    procMems.add(pssItem);
 
                     nativePss += mi.nativePss;
                     dalvikPss += mi.dalvikPss;
@@ -9161,6 +9168,10 @@
                     for (int oomIndex=0; oomIndex<oomPss.length; oomIndex++) {
                         if (r.setAdj <= oomAdj[oomIndex] || oomIndex == (oomPss.length-1)) {
                             oomPss[oomIndex] += myTotalPss;
+                            if (oomProcs[oomIndex] == null) {
+                                oomProcs[oomIndex] = new ArrayList<MemItem>();
+                            }
+                            oomProcs[oomIndex].add(pssItem);
                             break;
                         }
                     }
@@ -9181,7 +9192,9 @@
             ArrayList<MemItem> oomMems = new ArrayList<MemItem>();
             for (int j=0; j<oomPss.length; j++) {
                 if (oomPss[j] != 0) {
-                    oomMems.add(new MemItem(oomLabel[j], oomPss[j]));
+                    MemItem item = new MemItem(oomLabel[j], oomPss[j]);
+                    item.subitems = oomProcs[j];
+                    oomMems.add(item);
                 }
             }
 
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index 6b65e07..55e0678 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -388,6 +388,7 @@
         private final VpnConfig mConfig;
         private final String[] mDaemons;
         private final String[][] mArguments;
+        private final LocalSocket[] mSockets;
         private final String mOuterInterface;
         private final LegacyVpnInfo mInfo;
 
@@ -398,6 +399,7 @@
             mConfig = config;
             mDaemons = new String[] {"racoon", "mtpd"};
             mArguments = new String[][] {racoon, mtpd};
+            mSockets = new LocalSocket[mDaemons.length];
             mInfo = new LegacyVpnInfo();
 
             // This is the interface which VPN is running on.
@@ -416,10 +418,14 @@
         }
 
         public void exit() {
-            // We assume that everything is reset after the daemons die.
+            // We assume that everything is reset after stopping the daemons.
             interrupt();
-            for (String daemon : mDaemons) {
-                SystemProperties.set("ctl.stop", daemon);
+            for (LocalSocket socket : mSockets) {
+                try {
+                    socket.close();
+                } catch (Exception e) {
+                    // ignore
+                }
             }
         }
 
@@ -462,15 +468,10 @@
                 checkpoint(false);
                 mInfo.state = LegacyVpnInfo.STATE_INITIALIZING;
 
-                // First stop the daemons.
-                for (String daemon : mDaemons) {
-                    SystemProperties.set("ctl.stop", daemon);
-                }
-
                 // Wait for the daemons to stop.
                 for (String daemon : mDaemons) {
                     String key = "init.svc." + daemon;
-                    while (!"stopped".equals(SystemProperties.get(key))) {
+                    while (!"stopped".equals(SystemProperties.get(key, "stopped"))) {
                         checkpoint(true);
                     }
                 }
@@ -511,27 +512,27 @@
                     }
 
                     // Create the control socket.
-                    LocalSocket socket = new LocalSocket();
+                    mSockets[i] = new LocalSocket();
                     LocalSocketAddress address = new LocalSocketAddress(
                             daemon, LocalSocketAddress.Namespace.RESERVED);
 
                     // Wait for the socket to connect.
                     while (true) {
                         try {
-                            socket.connect(address);
+                            mSockets[i].connect(address);
                             break;
                         } catch (Exception e) {
                             // ignore
                         }
                         checkpoint(true);
                     }
-                    socket.setSoTimeout(500);
+                    mSockets[i].setSoTimeout(500);
 
                     // Send over the arguments.
-                    OutputStream out = socket.getOutputStream();
+                    OutputStream out = mSockets[i].getOutputStream();
                     for (String argument : arguments) {
                         byte[] bytes = argument.getBytes(Charsets.UTF_8);
-                        if (bytes.length > 0xFFFF) {
+                        if (bytes.length >= 0xFFFF) {
                             throw new IllegalArgumentException("Argument is too large");
                         }
                         out.write(bytes.length >> 8);
@@ -539,11 +540,12 @@
                         out.write(bytes);
                         checkpoint(false);
                     }
+                    out.write(0xFF);
+                    out.write(0xFF);
                     out.flush();
-                    socket.shutdownOutput();
 
                     // Wait for End-of-File.
-                    InputStream in = socket.getInputStream();
+                    InputStream in = mSockets[i].getInputStream();
                     while (true) {
                         try {
                             if (in.read() == -1) {
@@ -554,7 +556,6 @@
                         }
                         checkpoint(true);
                     }
-                    socket.close();
                 }
 
                 // Wait for the daemons to create the new state.
@@ -631,6 +632,13 @@
                 Log.i(TAG, "Aborting", e);
                 exit();
             } finally {
+                // Kill the daemons if they fail to stop.
+                if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING) {
+                    for (String daemon : mDaemons) {
+                        SystemProperties.set("ctl.stop", daemon);
+                    }
+                }
+
                 // Do not leave an unstable state.
                 if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING ||
                         mInfo.state == LegacyVpnInfo.STATE_CONNECTING) {
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 2f19a46..b8797d18 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -194,7 +194,8 @@
     /**
      * Whether verification is enabled by default.
      */
-    private static final boolean DEFAULT_VERIFY_ENABLE = true;
+    // STOPSHIP: change this to true
+    private static final boolean DEFAULT_VERIFY_ENABLE = false;
 
     /**
      * The default maximum time to wait for the verification agent to return in
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index a01c975..8f51466 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -568,12 +568,9 @@
                     notification.sound = null;
                     notification.vibrate = null;
 
-                    Intent intent = new Intent(
-                            Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
-                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
-                            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-                    intent.setComponent(new ComponentName("com.android.settings",
-                            "com.android.settings.UsbSettings"));
+                    Intent intent = Intent.makeRestartActivityTask(
+                            new ComponentName("com.android.settings",
+                                    "com.android.settings.UsbSettings"));
                     PendingIntent pi = PendingIntent.getActivity(mContext, 0,
                             intent, 0);
                     notification.setLatestEventInfo(mContext, title, message, pi);
@@ -604,12 +601,9 @@
                     notification.sound = null;
                     notification.vibrate = null;
 
-                    Intent intent = new Intent(
-                            Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
-                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
-                            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-                    intent.setComponent(new ComponentName("com.android.settings",
-                            "com.android.settings.DevelopmentSettings"));
+                    Intent intent = Intent.makeRestartActivityTask(
+                            new ComponentName("com.android.settings",
+                                    "com.android.settings.DevelopmentSettings"));
                     PendingIntent pi = PendingIntent.getActivity(mContext, 0,
                             intent, 0);
                     notification.setLatestEventInfo(mContext, title, message, pi);
diff --git a/services/sensorservice/Fusion.cpp b/services/sensorservice/Fusion.cpp
index ff4786b..0ab86c3 100644
--- a/services/sensorservice/Fusion.cpp
+++ b/services/sensorservice/Fusion.cpp
@@ -47,9 +47,46 @@
 static const float accSTDEV  = 0.05f;   // m/s^2 (measured 0.08 / CDD 0.05)
 static const float magSTDEV  = 0.5f;    // uT    (measured 0.7  / CDD 0.5)
 
-static const float FREE_FALL_THRESHOLD = 0.981f;
 static const float SYMMETRY_TOLERANCE = 1e-10f;
 
+/*
+ * Accelerometer updates will not be performed near free fall to avoid
+ * ill-conditioning and div by zeros.
+ * Threshhold: 10% of g, in m/s^2
+ */
+static const float FREE_FALL_THRESHOLD = 0.981f;
+static const float FREE_FALL_THRESHOLD_SQ =
+        FREE_FALL_THRESHOLD*FREE_FALL_THRESHOLD;
+
+/*
+ * The geomagnetic-field should be between 30uT and 60uT.
+ * Fields strengths greater than this likely indicate a local magnetic
+ * disturbance which we do not want to update into the fused frame.
+ */
+static const float MAX_VALID_MAGNETIC_FIELD = 100; // uT
+static const float MAX_VALID_MAGNETIC_FIELD_SQ =
+        MAX_VALID_MAGNETIC_FIELD*MAX_VALID_MAGNETIC_FIELD;
+
+/*
+ * Values of the field smaller than this should be ignored in fusion to avoid
+ * ill-conditioning. This state can happen with anomalous local magnetic
+ * disturbances canceling the Earth field.
+ */
+static const float MIN_VALID_MAGNETIC_FIELD = 10; // uT
+static const float MIN_VALID_MAGNETIC_FIELD_SQ =
+        MIN_VALID_MAGNETIC_FIELD*MIN_VALID_MAGNETIC_FIELD;
+
+/*
+ * If the cross product of two vectors has magnitude squared less than this,
+ * we reject it as invalid due to alignment of the vectors.
+ * This threshold is used to check for the case where the magnetic field sample
+ * is parallel to the gravity field, which can happen in certain places due
+ * to magnetic field disturbances.
+ */
+static const float MIN_VALID_CROSS_PRODUCT_MAG = 1.0e-3;
+static const float MIN_VALID_CROSS_PRODUCT_MAG_SQ =
+    MIN_VALID_CROSS_PRODUCT_MAG*MIN_VALID_CROSS_PRODUCT_MAG;
+
 // -----------------------------------------------------------------------
 
 template <typename TYPE, size_t C, size_t R>
@@ -240,8 +277,9 @@
 
 status_t Fusion::handleAcc(const vec3_t& a) {
     // ignore acceleration data if we're close to free-fall
-    if (length(a) < FREE_FALL_THRESHOLD)
+    if (length_squared(a) < FREE_FALL_THRESHOLD_SQ) {
         return BAD_VALUE;
+    }
 
     if (!checkInitComplete(ACC, a))
         return BAD_VALUE;
@@ -253,15 +291,34 @@
 
 status_t Fusion::handleMag(const vec3_t& m) {
     // the geomagnetic-field should be between 30uT and 60uT
-    // reject obviously wrong magnetic-fields
-    if (length(m) > 100)
+    // reject if too large to avoid spurious magnetic sources
+    const float magFieldSq = length_squared(m);
+    if (magFieldSq > MAX_VALID_MAGNETIC_FIELD_SQ) {
         return BAD_VALUE;
+    } else if (magFieldSq < MIN_VALID_MAGNETIC_FIELD_SQ) {
+        // Also reject if too small since we will get ill-defined (zero mag)
+        // cross-products below
+        return BAD_VALUE;
+    }
 
     if (!checkInitComplete(MAG, m))
         return BAD_VALUE;
 
+    // Orthogonalize the magnetic field to the gravity field, mapping it into
+    // tangent to Earth.
     const vec3_t up( getRotationMatrix() * Ba );
     const vec3_t east( cross_product(m, up) );
+
+    // If the m and up vectors align, the cross product magnitude will
+    // approach 0.
+    // Reject this case as well to avoid div by zero problems and
+    // ill-conditioning below.
+    if (length_squared(east) < MIN_VALID_CROSS_PRODUCT_MAG_SQ) {
+        return BAD_VALUE;
+    }
+
+    // If we have created an orthogonal magnetic field successfully,
+    // then pass it in as the update.
     vec3_t north( cross_product(up, east) );
 
     const float l = 1 / length(north);
diff --git a/services/sensorservice/vec.h b/services/sensorservice/vec.h
index f74ccc5..24f30ff 100644
--- a/services/sensorservice/vec.h
+++ b/services/sensorservice/vec.h
@@ -212,6 +212,15 @@
     typename TYPE,
     size_t SIZE
 >
+TYPE PURE length_squared(const V<TYPE, SIZE>& v) {
+    return dot_product(v, v);
+}
+
+template <
+    template<typename T, size_t S> class V,
+    typename TYPE,
+    size_t SIZE
+>
 V<TYPE, SIZE> PURE normalize(const V<TYPE, SIZE>& v) {
     return v * (1/length(v));
 }
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 8b2485a..f8925b8 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -556,6 +556,7 @@
         // need a hardware-protected path to external video sink
         usage |= GraphicBuffer::USAGE_PROTECTED;
     }
+    usage |= GraphicBuffer::USAGE_HW_COMPOSER;
     return usage;
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 0080202..6fde361 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2207,7 +2207,7 @@
         return BAD_VALUE;
 
     // make sure none of the layers are protected
-    const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
+    const LayerVector& layers(mDrawingState.layersSortedByZ);
     const size_t count = layers.size();
     for (size_t i=0 ; i<count ; ++i) {
         const sp<LayerBase>& layer(layers[i]);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
index e77178d..3232eedc 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
@@ -33,7 +33,6 @@
 import android.widget.FrameLayout;
 
 import javax.microedition.khronos.egl.EGL10;
-import javax.microedition.khronos.egl.EGL11;
 import javax.microedition.khronos.egl.EGLConfig;
 import javax.microedition.khronos.egl.EGLContext;
 import javax.microedition.khronos.egl.EGLDisplay;
@@ -207,7 +206,7 @@
             glEnableVertexAttribArray(attribTexCoords);
             checkGlError();
 
-            glUniform1i(texture, 0);
+            glUniform1i(uniformTexture, texture);
             checkGlError();
             
             while (!mFinished) {
@@ -350,7 +349,7 @@
                     !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
                 if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
                     throw new RuntimeException("eglMakeCurrent failed "
-                            + getEGLErrorString(mEgl.eglGetError()));
+                            + GLUtils.getEGLErrorString(mEgl.eglGetError()));
                 }
             }
         }
@@ -361,13 +360,13 @@
             mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
             if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
                 throw new RuntimeException("eglGetDisplay failed "
-                        + getEGLErrorString(mEgl.eglGetError()));
+                        + GLUtils.getEGLErrorString(mEgl.eglGetError()));
             }
             
             int[] version = new int[2];
             if (!mEgl.eglInitialize(mEglDisplay, version)) {
                 throw new RuntimeException("eglInitialize failed " +
-                        getEGLErrorString(mEgl.eglGetError()));
+                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
             }
 
             mEglConfig = chooseEglConfig();
@@ -386,12 +385,12 @@
                     return;
                 }
                 throw new RuntimeException("createWindowSurface failed "
-                        + getEGLErrorString(error));
+                        + GLUtils.getEGLErrorString(error));
             }
 
             if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
                 throw new RuntimeException("eglMakeCurrent failed "
-                        + getEGLErrorString(mEgl.eglGetError()));
+                        + GLUtils.getEGLErrorString(mEgl.eglGetError()));
             }
 
             mGL = mEglContext.getGL();
@@ -409,7 +408,7 @@
             int[] configSpec = getConfig();
             if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
                 throw new IllegalArgumentException("eglChooseConfig failed " +
-                        getEGLErrorString(mEgl.eglGetError()));
+                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
             } else if (configsCount[0] > 0) {
                 return configs[0];
             }
@@ -429,43 +428,6 @@
             };
         }
 
-        static String getEGLErrorString(int error) {
-            switch (error) {
-                case EGL10.EGL_SUCCESS:
-                    return "EGL_SUCCESS";
-                case EGL10.EGL_NOT_INITIALIZED:
-                    return "EGL_NOT_INITIALIZED";
-                case EGL10.EGL_BAD_ACCESS:
-                    return "EGL_BAD_ACCESS";
-                case EGL10.EGL_BAD_ALLOC:
-                    return "EGL_BAD_ALLOC";
-                case EGL10.EGL_BAD_ATTRIBUTE:
-                    return "EGL_BAD_ATTRIBUTE";
-                case EGL10.EGL_BAD_CONFIG:
-                    return "EGL_BAD_CONFIG";
-                case EGL10.EGL_BAD_CONTEXT:
-                    return "EGL_BAD_CONTEXT";
-                case EGL10.EGL_BAD_CURRENT_SURFACE:
-                    return "EGL_BAD_CURRENT_SURFACE";
-                case EGL10.EGL_BAD_DISPLAY:
-                    return "EGL_BAD_DISPLAY";
-                case EGL10.EGL_BAD_MATCH:
-                    return "EGL_BAD_MATCH";
-                case EGL10.EGL_BAD_NATIVE_PIXMAP:
-                    return "EGL_BAD_NATIVE_PIXMAP";
-                case EGL10.EGL_BAD_NATIVE_WINDOW:
-                    return "EGL_BAD_NATIVE_WINDOW";
-                case EGL10.EGL_BAD_PARAMETER:
-                    return "EGL_BAD_PARAMETER";
-                case EGL10.EGL_BAD_SURFACE:
-                    return "EGL_BAD_SURFACE";
-                case EGL11.EGL_CONTEXT_LOST:
-                    return "EGL_CONTEXT_LOST";
-                default:
-                    return "0x" + Integer.toHexString(error);
-            }
-        }
-
         void finish() {
             mFinished = true;
         }
diff --git a/tests/RenderScriptTests/FBOTest/res/drawable/robot.png b/tests/RenderScriptTests/FBOTest/res/drawable-nodpi/robot.png
similarity index 100%
rename from tests/RenderScriptTests/FBOTest/res/drawable/robot.png
rename to tests/RenderScriptTests/FBOTest/res/drawable-nodpi/robot.png
Binary files differ
diff --git a/tests/RenderScriptTests/ImageProcessing/res/drawable/city.png b/tests/RenderScriptTests/ImageProcessing/res/drawable-nodpi/city.png
similarity index 100%
rename from tests/RenderScriptTests/ImageProcessing/res/drawable/city.png
rename to tests/RenderScriptTests/ImageProcessing/res/drawable-nodpi/city.png
Binary files differ
diff --git a/tests/RenderScriptTests/ModelViewer/res/drawable/robot.png b/tests/RenderScriptTests/ModelViewer/res/drawable-nodpi/robot.png
similarity index 100%
rename from tests/RenderScriptTests/ModelViewer/res/drawable/robot.png
rename to tests/RenderScriptTests/ModelViewer/res/drawable-nodpi/robot.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/checker.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/checker.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/checker.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/checker.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/data.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/data.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/data.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/data.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/flares.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/flares.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/flares.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/flares.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/globe.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/globe.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/globe.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/globe.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/leaf.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/leaf.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/leaf.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/leaf.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/light1.jpg b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/light1.jpg
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/light1.jpg
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/light1.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/space.jpg b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/space.jpg
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/space.jpg
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/space.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/test_pattern.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/test_pattern.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/test_pattern.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/test_pattern.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/torusmap.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/torusmap.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/torusmap.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/torusmap.png
Binary files differ
diff --git a/tests/RenderScriptTests/tests/res/drawable/test_pattern.png b/tests/RenderScriptTests/tests/res/drawable-nodpi/test_pattern.png
similarity index 100%
rename from tests/RenderScriptTests/tests/res/drawable/test_pattern.png
rename to tests/RenderScriptTests/tests/res/drawable-nodpi/test_pattern.png
Binary files differ