Merge "Ensure only enabled profile apps can be launched"
diff --git a/api/current.txt b/api/current.txt
index 17f5e53..eb1f46b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -250,7 +250,7 @@
     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 actionBarTheme = 16843831; // 0x1010437
+    field public static final int actionBarTheme = 16843829; // 0x1010435
     field public static final int actionBarWidgetTheme = 16843671; // 0x1010397
     field public static final int actionButtonStyle = 16843480; // 0x10102d8
     field public static final int actionDropDownStyle = 16843479; // 0x10102d7
@@ -267,7 +267,7 @@
     field public static final int actionModeSplitBackground = 16843677; // 0x101039d
     field public static final int actionModeStyle = 16843668; // 0x1010394
     field public static final int actionOverflowButtonStyle = 16843510; // 0x10102f6
-    field public static final int actionOverflowMenuStyle = 16843851; // 0x101044b
+    field public static final int actionOverflowMenuStyle = 16843849; // 0x1010449
     field public static final int actionProviderClass = 16843657; // 0x1010389
     field public static final int actionViewClass = 16843516; // 0x10102fc
     field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd
@@ -313,7 +313,7 @@
     field public static final int autoCompleteTextViewStyle = 16842859; // 0x101006b
     field public static final int autoLink = 16842928; // 0x10100b0
     field public static final int autoMirrored = 16843754; // 0x10103ea
-    field public static final int autoRemoveFromRecents = 16843853; // 0x101044d
+    field public static final int autoRemoveFromRecents = 16843851; // 0x101044b
     field public static final int autoStart = 16843445; // 0x10102b5
     field public static final deprecated int autoText = 16843114; // 0x101016a
     field public static final int autoUrlDetect = 16843404; // 0x101028c
@@ -386,14 +386,12 @@
     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 colorAccent = 16843836; // 0x101043c
+    field public static final int colorAccent = 16843834; // 0x101043a
     field public static final int colorActivatedHighlight = 16843664; // 0x1010390
     field public static final int colorBackground = 16842801; // 0x1010031
     field public static final int colorBackgroundCacheHint = 16843435; // 0x10102ab
     field public static final int colorButtonNormal = 16843823; // 0x101042f
-    field public static final int colorButtonNormalColored = 16843825; // 0x1010431
     field public static final int colorButtonPressed = 16843824; // 0x1010430
-    field public static final int colorButtonPressedColored = 16843826; // 0x1010432
     field public static final int colorControlActivated = 16843822; // 0x101042e
     field public static final int colorControlNormal = 16843821; // 0x101042d
     field public static final int colorFocusedHighlight = 16843663; // 0x101038f
@@ -402,9 +400,9 @@
     field public static final int colorLongPressedHighlight = 16843662; // 0x101038e
     field public static final int colorMultiSelectHighlight = 16843665; // 0x1010391
     field public static final int colorPressedHighlight = 16843661; // 0x101038d
-    field public static final int colorPrimary = 16843834; // 0x101043a
-    field public static final int colorPrimaryDark = 16843835; // 0x101043b
-    field public static final int colorPrimaryLight = 16843833; // 0x1010439
+    field public static final int colorPrimary = 16843832; // 0x1010438
+    field public static final int colorPrimaryDark = 16843833; // 0x1010439
+    field public static final int colorPrimaryLight = 16843831; // 0x1010437
     field public static final int columnCount = 16843639; // 0x1010377
     field public static final int columnDelay = 16843215; // 0x10101cf
     field public static final int columnOrderPreserved = 16843640; // 0x1010378
@@ -463,7 +461,7 @@
     field public static final int dividerHorizontal = 16843564; // 0x101032c
     field public static final int dividerPadding = 16843562; // 0x101032a
     field public static final int dividerVertical = 16843530; // 0x101030a
-    field public static final int documentLaunchMode = 16843852; // 0x101044c
+    field public static final int documentLaunchMode = 16843850; // 0x101044a
     field public static final int drawSelectorOnTop = 16843004; // 0x10100fc
     field public static final int drawable = 16843161; // 0x1010199
     field public static final int drawableBottom = 16843118; // 0x101016e
@@ -492,7 +490,7 @@
     field public static final int editTextStyle = 16842862; // 0x101006e
     field public static final deprecated int editable = 16843115; // 0x101016b
     field public static final int editorExtras = 16843300; // 0x1010224
-    field public static final int elevation = 16843847; // 0x1010447
+    field public static final int elevation = 16843845; // 0x1010445
     field public static final int ellipsize = 16842923; // 0x10100ab
     field public static final int ems = 16843096; // 0x1010158
     field public static final int enabled = 16842766; // 0x101000e
@@ -502,9 +500,10 @@
     field public static final int entries = 16842930; // 0x10100b2
     field public static final int entryValues = 16843256; // 0x10101f8
     field public static final int eventsInterceptionEnabled = 16843389; // 0x101027d
-    field public static final int excludeClass = 16843849; // 0x1010449
+    field public static final int excludeClass = 16843847; // 0x1010447
     field public static final int excludeFromRecents = 16842775; // 0x1010017
-    field public static final int excludeId = 16843848; // 0x1010448
+    field public static final int excludeId = 16843846; // 0x1010446
+    field public static final int excludeViewName = 16843858; // 0x1010452
     field public static final int exitFadeDuration = 16843533; // 0x101030d
     field public static final int expandableListPreferredChildIndicatorLeft = 16842834; // 0x1010052
     field public static final int expandableListPreferredChildIndicatorRight = 16842835; // 0x1010053
@@ -567,7 +566,7 @@
     field public static final int freezesText = 16843116; // 0x101016c
     field public static final int fromAlpha = 16843210; // 0x10101ca
     field public static final int fromDegrees = 16843187; // 0x10101b3
-    field public static final int fromId = 16843856; // 0x1010450
+    field public static final int fromId = 16843854; // 0x101044e
     field public static final int fromScene = 16843741; // 0x10103dd
     field public static final int fromXDelta = 16843206; // 0x10101c6
     field public static final int fromXScale = 16843202; // 0x10101c2
@@ -600,7 +599,7 @@
     field public static final int headerBackground = 16843055; // 0x101012f
     field public static final int headerDividersEnabled = 16843310; // 0x101022e
     field public static final int height = 16843093; // 0x1010155
-    field public static final int hideOnContentScroll = 16843850; // 0x101044a
+    field public static final int hideOnContentScroll = 16843848; // 0x1010448
     field public static final int hint = 16843088; // 0x1010150
     field public static final int homeAsUpIndicator = 16843531; // 0x101030b
     field public static final int homeLayout = 16843549; // 0x101031d
@@ -831,7 +830,7 @@
     field public static final int name = 16842755; // 0x1010003
     field public static final int navigationMode = 16843471; // 0x10102cf
     field public static final int negativeButtonText = 16843254; // 0x10101f6
-    field public static final int nestedScrollingEnabled = 16843837; // 0x101043d
+    field public static final int nestedScrollingEnabled = 16843835; // 0x101043b
     field public static final int nextFocusDown = 16842980; // 0x10100e4
     field public static final int nextFocusForward = 16843580; // 0x101033c
     field public static final int nextFocusLeft = 16842977; // 0x10100e1
@@ -880,7 +879,7 @@
     field public static final int permissionFlags = 16843719; // 0x10103c7
     field public static final int permissionGroup = 16842762; // 0x101000a
     field public static final int permissionGroupFlags = 16843717; // 0x10103c5
-    field public static final int persistable = 16843827; // 0x1010433
+    field public static final int persistable = 16843825; // 0x1010431
     field public static final int persistent = 16842765; // 0x101000d
     field public static final int persistentDrawingCache = 16842990; // 0x10100ee
     field public static final deprecated int phoneNumber = 16843111; // 0x1010167
@@ -957,7 +956,7 @@
     field public static final int restoreAnyVersion = 16843450; // 0x10102ba
     field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
     field public static final int restrictedAccountType = 16843733; // 0x10103d5
-    field public static final int reversible = 16843857; // 0x1010451
+    field public static final int reversible = 16843855; // 0x101044f
     field public static final int right = 16843183; // 0x10101af
     field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093
     field public static final int ringtoneType = 16843257; // 0x10101f9
@@ -1013,7 +1012,7 @@
     field public static final int selectableItemBackground = 16843534; // 0x101030e
     field public static final int selectedDateVerticalBar = 16843591; // 0x1010347
     field public static final int selectedWeekBackgroundColor = 16843586; // 0x1010342
-    field public static final int sessionService = 16843844; // 0x1010444
+    field public static final int sessionService = 16843842; // 0x1010442
     field public static final int settingsActivity = 16843301; // 0x1010225
     field public static final int shadowColor = 16843105; // 0x1010161
     field public static final int shadowDx = 16843106; // 0x1010162
@@ -1021,7 +1020,6 @@
     field public static final int shadowRadius = 16843108; // 0x1010164
     field public static final int shape = 16843162; // 0x101019a
     field public static final int shareInterpolator = 16843195; // 0x10101bb
-    field public static final int sharedElementName = 16843803; // 0x101041b
     field public static final int sharedUserId = 16842763; // 0x101000b
     field public static final int sharedUserLabel = 16843361; // 0x1010261
     field public static final int shouldDisableView = 16843246; // 0x10101ee
@@ -1035,7 +1033,7 @@
     field public static final int shrinkColumns = 16843082; // 0x101014a
     field public static final deprecated int singleLine = 16843101; // 0x101015d
     field public static final int singleUser = 16843711; // 0x10103bf
-    field public static final int slideEdge = 16843830; // 0x1010436
+    field public static final int slideEdge = 16843828; // 0x1010434
     field public static final int smallIcon = 16843422; // 0x101029e
     field public static final int smallScreens = 16843396; // 0x1010284
     field public static final int smoothScrollbar = 16843313; // 0x1010231
@@ -1047,19 +1045,19 @@
     field public static final int spinnerStyle = 16842881; // 0x1010081
     field public static final int spinnersShown = 16843595; // 0x101034b
     field public static final int splitMotionEvents = 16843503; // 0x10102ef
-    field public static final int splitTrack = 16843858; // 0x1010452
+    field public static final int splitTrack = 16843856; // 0x1010450
     field public static final int src = 16843033; // 0x1010119
     field public static final int ssp = 16843747; // 0x10103e3
     field public static final int sspPattern = 16843749; // 0x10103e5
     field public static final int sspPrefix = 16843748; // 0x10103e4
     field public static final int stackFromBottom = 16843005; // 0x10100fd
-    field public static final int stackViewStyle = 16843845; // 0x1010445
+    field public static final int stackViewStyle = 16843843; // 0x1010443
     field public static final int starStyle = 16842882; // 0x1010082
     field public static final int startColor = 16843165; // 0x101019d
     field public static final int startDelay = 16843746; // 0x10103e2
     field public static final int startOffset = 16843198; // 0x10101be
     field public static final deprecated int startYear = 16843132; // 0x101017c
-    field public static final int stateListAnimator = 16843854; // 0x101044e
+    field public static final int stateListAnimator = 16843852; // 0x101044c
     field public static final int stateNotNeeded = 16842774; // 0x1010016
     field public static final int state_above_anchor = 16842922; // 0x10100aa
     field public static final int state_accelerated = 16843547; // 0x101031b
@@ -1095,7 +1093,7 @@
     field public static final int strokeOpacity = 16843811; // 0x1010423
     field public static final int strokeWidth = 16843812; // 0x1010424
     field public static final int subtitle = 16843473; // 0x10102d1
-    field public static final int subtitleTextAppearance = 16843829; // 0x1010435
+    field public static final int subtitleTextAppearance = 16843827; // 0x1010433
     field public static final int subtitleTextStyle = 16843513; // 0x10102f9
     field public static final int subtypeExtraValue = 16843674; // 0x101039a
     field public static final int subtypeId = 16843713; // 0x10103c1
@@ -1112,7 +1110,7 @@
     field public static final int switchMinWidth = 16843632; // 0x1010370
     field public static final int switchPadding = 16843633; // 0x1010371
     field public static final int switchPreferenceStyle = 16843629; // 0x101036d
-    field public static final int switchStyle = 16843846; // 0x1010446
+    field public static final int switchStyle = 16843844; // 0x1010444
     field public static final int switchTextAppearance = 16843630; // 0x101036e
     field public static final int switchTextOff = 16843628; // 0x101036c
     field public static final int switchTextOn = 16843627; // 0x101036b
@@ -1128,6 +1126,7 @@
     field public static final int targetId = 16843740; // 0x10103dc
     field public static final int targetPackage = 16842785; // 0x1010021
     field public static final int targetSdkVersion = 16843376; // 0x1010270
+    field public static final int targetViewName = 16843857; // 0x1010451
     field public static final int taskAffinity = 16842770; // 0x1010012
     field public static final int taskCloseEnterAnimation = 16842942; // 0x10100be
     field public static final int taskCloseExitAnimation = 16842943; // 0x10100bf
@@ -1149,7 +1148,7 @@
     field public static final int textAppearanceLargeInverse = 16842819; // 0x1010043
     field public static final int textAppearanceLargePopupMenu = 16843521; // 0x1010301
     field public static final int textAppearanceListItem = 16843678; // 0x101039e
-    field public static final int textAppearanceListItemSecondary = 16843832; // 0x1010438
+    field public static final int textAppearanceListItemSecondary = 16843830; // 0x1010436
     field public static final int textAppearanceListItemSmall = 16843679; // 0x101039f
     field public static final int textAppearanceMedium = 16842817; // 0x1010041
     field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044
@@ -1213,11 +1212,11 @@
     field public static final int tintMode = 16843798; // 0x1010416
     field public static final int title = 16843233; // 0x10101e1
     field public static final int titleCondensed = 16843234; // 0x10101e2
-    field public static final int titleTextAppearance = 16843828; // 0x1010434
+    field public static final int titleTextAppearance = 16843826; // 0x1010432
     field public static final int titleTextStyle = 16843512; // 0x10102f8
     field public static final int toAlpha = 16843211; // 0x10101cb
     field public static final int toDegrees = 16843188; // 0x10101b4
-    field public static final int toId = 16843855; // 0x101044f
+    field public static final int toId = 16843853; // 0x101044d
     field public static final int toScene = 16843742; // 0x10103de
     field public static final int toXDelta = 16843207; // 0x10101c7
     field public static final int toXScale = 16843203; // 0x10101c3
@@ -1266,6 +1265,7 @@
     field public static final int verticalGap = 16843328; // 0x1010240
     field public static final int verticalScrollbarPosition = 16843572; // 0x1010334
     field public static final int verticalSpacing = 16843029; // 0x1010115
+    field public static final int viewName = 16843803; // 0x101041b
     field public static final int viewportHeight = 16843806; // 0x101041e
     field public static final int viewportWidth = 16843805; // 0x101041d
     field public static final int visibility = 16842972; // 0x10100dc
@@ -1296,8 +1296,8 @@
     field public static final int windowActionBar = 16843469; // 0x10102cd
     field public static final int windowActionBarOverlay = 16843492; // 0x10102e4
     field public static final int windowActionModeOverlay = 16843485; // 0x10102dd
-    field public static final int windowAllowEnterTransitionOverlap = 16843843; // 0x1010443
-    field public static final int windowAllowExitTransitionOverlap = 16843842; // 0x1010442
+    field public static final int windowAllowEnterTransitionOverlap = 16843841; // 0x1010441
+    field public static final int windowAllowExitTransitionOverlap = 16843840; // 0x1010440
     field public static final int windowAnimationStyle = 16842926; // 0x10100ae
     field public static final int windowBackground = 16842836; // 0x1010054
     field public static final int windowCloseOnTouchOutside = 16843611; // 0x101035b
@@ -1307,9 +1307,9 @@
     field public static final int windowDisablePreview = 16843298; // 0x1010222
     field public static final int windowEnableSplitTouch = 16843543; // 0x1010317
     field public static final int windowEnterAnimation = 16842932; // 0x10100b4
-    field public static final int windowEnterTransition = 16843838; // 0x101043e
+    field public static final int windowEnterTransition = 16843836; // 0x101043c
     field public static final int windowExitAnimation = 16842933; // 0x10100b5
-    field public static final int windowExitTransition = 16843839; // 0x101043f
+    field public static final int windowExitTransition = 16843837; // 0x101043d
     field public static final int windowFrame = 16842837; // 0x1010055
     field public static final int windowFullscreen = 16843277; // 0x101020d
     field public static final int windowHideAnimation = 16842935; // 0x10100b7
@@ -1320,8 +1320,8 @@
     field public static final int windowNoDisplay = 16843294; // 0x101021e
     field public static final int windowNoTitle = 16842838; // 0x1010056
     field public static final int windowOverscan = 16843727; // 0x10103cf
-    field public static final int windowSharedElementEnterTransition = 16843840; // 0x1010440
-    field public static final int windowSharedElementExitTransition = 16843841; // 0x1010441
+    field public static final int windowSharedElementEnterTransition = 16843838; // 0x101043e
+    field public static final int windowSharedElementExitTransition = 16843839; // 0x101043f
     field public static final int windowShowAnimation = 16842934; // 0x10100b6
     field public static final int windowShowWallpaper = 16843410; // 0x1010292
     field public static final int windowSoftInputMode = 16843307; // 0x101022b
@@ -1627,7 +1627,7 @@
     field public static final int l_resource_pad8 = 16908345; // 0x1020039
     field public static final int l_resource_pad9 = 16908344; // 0x1020038
     field public static final int list = 16908298; // 0x102000a
-    field public static final int mask = 16908354; // 0x1020042
+    field public static final int mask = 16908353; // 0x1020041
     field public static final int message = 16908299; // 0x102000b
     field public static final int paste = 16908322; // 0x1020022
     field public static final int primary = 16908300; // 0x102000c
@@ -1636,8 +1636,7 @@
     field public static final int selectAll = 16908319; // 0x102001f
     field public static final int selectTextMode = 16908333; // 0x102002d
     field public static final int selectedIcon = 16908302; // 0x102000e
-    field public static final int shared_element = 16908355; // 0x1020043
-    field public static final int shared_element_name = 16908353; // 0x1020041
+    field public static final int shared_element = 16908354; // 0x1020042
     field public static final int startSelectingText = 16908328; // 0x1020028
     field public static final int stopSelectingText = 16908329; // 0x1020029
     field public static final int summary = 16908304; // 0x1020010
@@ -19643,7 +19642,7 @@
     field public static final int JELLY_BEAN_MR1 = 17; // 0x11
     field public static final int JELLY_BEAN_MR2 = 18; // 0x12
     field public static final int KITKAT = 19; // 0x13
-    field public static final int KITKAT_WATCH = 10000; // 0x2710
+    field public static final int KITKAT_WATCH = 20; // 0x14
     field public static final int L = 10000; // 0x2710
   }
 
@@ -29055,6 +29054,7 @@
     ctor public Transition();
     method public android.transition.Transition addListener(android.transition.Transition.TransitionListener);
     method public android.transition.Transition addTarget(int);
+    method public android.transition.Transition addTarget(java.lang.String);
     method public android.transition.Transition addTarget(java.lang.Class);
     method public android.transition.Transition addTarget(android.view.View);
     method public boolean canRemoveViews();
@@ -29066,6 +29066,7 @@
     method public android.transition.Transition excludeChildren(android.view.View, boolean);
     method public android.transition.Transition excludeChildren(java.lang.Class, boolean);
     method public android.transition.Transition excludeTarget(int, boolean);
+    method public android.transition.Transition excludeTarget(java.lang.String, boolean);
     method public android.transition.Transition excludeTarget(android.view.View, boolean);
     method public android.transition.Transition excludeTarget(java.lang.Class, boolean);
     method public long getDuration();
@@ -29076,12 +29077,16 @@
     method public android.transition.TransitionPropagation getPropagation();
     method public long getStartDelay();
     method public java.util.List<java.lang.Integer> getTargetIds();
+    method public java.util.List<java.lang.Class> getTargetTypes();
+    method public java.util.List<java.lang.String> getTargetViewNames();
     method public java.util.List<android.view.View> getTargets();
     method public java.lang.String[] getTransitionProperties();
     method public android.transition.TransitionValues getTransitionValues(android.view.View, boolean);
     method public android.transition.Transition removeListener(android.transition.Transition.TransitionListener);
     method public android.transition.Transition removeTarget(int);
+    method public android.transition.Transition removeTarget(java.lang.String);
     method public android.transition.Transition removeTarget(android.view.View);
+    method public android.transition.Transition removeTarget(java.lang.Class);
     method public android.transition.Transition setDuration(long);
     method public void setEpicenterCallback(android.transition.Transition.EpicenterCallback);
     method public android.transition.Transition setInterpolator(android.animation.TimeInterpolator);
@@ -31311,7 +31316,6 @@
     method public int getScrollBarStyle();
     method public final int getScrollX();
     method public final int getScrollY();
-    method public java.lang.String getSharedElementName();
     method public int getSolidColor();
     method public android.animation.StateListAnimator getStateListAnimator();
     method protected int getSuggestedMinimumHeight();
@@ -31332,6 +31336,7 @@
     method public int getVerticalFadingEdgeLength();
     method public int getVerticalScrollbarPosition();
     method public int getVerticalScrollbarWidth();
+    method public java.lang.String getViewName();
     method public android.view.ViewTreeObserver getViewTreeObserver();
     method public int getVisibility();
     method public final int getWidth();
@@ -31576,7 +31581,6 @@
     method public void setScrollY(int);
     method public void setScrollbarFadingEnabled(boolean);
     method public void setSelected(boolean);
-    method public void setSharedElementName(java.lang.String);
     method public void setSoundEffectsEnabled(boolean);
     method public void setStateListAnimator(android.animation.StateListAnimator);
     method public void setSystemUiVisibility(int);
@@ -31592,6 +31596,7 @@
     method public void setVerticalFadingEdgeEnabled(boolean);
     method public void setVerticalScrollBarEnabled(boolean);
     method public void setVerticalScrollbarPosition(int);
+    method public final void setViewName(java.lang.String);
     method public void setVisibility(int);
     method public void setWillNotCacheDrawing(boolean);
     method public void setWillNotDraw(boolean);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index b4b3c99..4a30b05 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -30,7 +30,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
-import android.app.admin.DevicePolicyManager;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -1150,6 +1149,12 @@
         }
 
         getApplication().dispatchActivityStarted(this);
+
+        final ActivityOptions activityOptions = getActivityOptions();
+        if (activityOptions != null &&
+                activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
+            mEnterTransitionCoordinator = activityOptions.createEnterActivityTransition(this);
+        }
     }
 
     /**
@@ -5272,19 +5277,29 @@
      *
      * @param callback the method to call when all visible Activities behind this one have been
      * drawn and it is safe to make this Activity translucent again.
+     * @param options activity options delivered to the activity below this one. The options
+     * are retrieved using {@link #getActivityOptions}.
      *
      * @see #convertFromTranslucent()
      * @see TranslucentConversionListener
      *
      * @hide
      */
-    public void convertToTranslucent(TranslucentConversionListener callback) {
+    void convertToTranslucent(TranslucentConversionListener callback, ActivityOptions options) {
+        boolean drawComplete;
         try {
             mTranslucentCallback = callback;
             mChangeCanvasToTranslucent =
-                    ActivityManagerNative.getDefault().convertToTranslucent(mToken);
+                    ActivityManagerNative.getDefault().convertToTranslucent(mToken, options);
+            drawComplete = true;
         } catch (RemoteException e) {
-            // pass
+            // Make callback return as though it timed out.
+            mChangeCanvasToTranslucent = false;
+            drawComplete = false;
+        }
+        if (!mChangeCanvasToTranslucent && mTranslucentCallback != null) {
+            // Window is already translucent.
+            mTranslucentCallback.onTranslucentConversionComplete(drawComplete);
         }
     }
 
@@ -5300,6 +5315,22 @@
     }
 
     /**
+     * Retrieve the ActivityOptions passed in from the launching activity or passed back
+     * from an activity launched by this activity in its call to {@link
+     * #convertToTranslucent(TranslucentConversionListener, ActivityOptions)}
+     *
+     * @return The ActivityOptions passed to {@link #convertToTranslucent}.
+     * @hide
+     */
+    ActivityOptions getActivityOptions() {
+        try {
+            return ActivityManagerNative.getDefault().getActivityOptions(mToken);
+        } catch (RemoteException e) {
+        }
+        return null;
+    }
+
+    /**
      * Adjust the current immersive mode setting.
      *
      * Note that changing this value will have no effect on the activity's
@@ -5533,30 +5564,12 @@
         mParent = parent;
     }
 
-    final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token,
-            Application application, Intent intent, ActivityInfo info, CharSequence title, 
-            Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances,
-            Configuration config) {
-        attach(context, aThread, instr, token, 0, application, intent, info, title, parent, id,
-            lastNonConfigurationInstances, config);
-    }
-    
     final void attach(Context context, ActivityThread aThread,
             Instrumentation instr, IBinder token, int ident,
             Application application, Intent intent, ActivityInfo info,
             CharSequence title, Activity parent, String id,
             NonConfigurationInstances lastNonConfigurationInstances,
-            Configuration config) {
-        attach(context, aThread, instr, token, ident, application, intent, info, title, parent, id,
-                lastNonConfigurationInstances, config, null, null);
-    }
-
-    final void attach(Context context, ActivityThread aThread,
-            Instrumentation instr, IBinder token, int ident,
-            Application application, Intent intent, ActivityInfo info,
-            CharSequence title, Activity parent, String id,
-            NonConfigurationInstances lastNonConfigurationInstances,
-            Configuration config, Bundle options, IVoiceInteractor voiceInteractor) {
+            Configuration config, IVoiceInteractor voiceInteractor) {
         attachBaseContext(context);
 
         mFragments.attachActivity(this, mContainer, null);
@@ -5597,12 +5610,6 @@
         }
         mWindowManager = mWindow.getWindowManager();
         mCurrentConfig = config;
-        if (options != null) {
-            ActivityOptions activityOptions = new ActivityOptions(options);
-            if (activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
-                mEnterTransitionCoordinator = activityOptions.createEnterActivityTransition(this);
-            }
-        }
     }
 
     /** @hide */
@@ -5873,7 +5880,7 @@
          * occurred waiting for the Activity to complete drawing.
          *
          * @see Activity#convertFromTranslucent()
-         * @see Activity#convertToTranslucent(TranslucentConversionListener)
+         * @see Activity#convertToTranslucent(TranslucentConversionListener, ActivityOptions)
          */
         public void onTranslucentConversionComplete(boolean drawComplete);
     }
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index ec2868a..2f924d3 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1114,7 +1114,8 @@
             int pid = data.readInt();
             int uid = data.readInt();
             int mode = data.readInt();
-            int res = checkUriPermission(uri, pid, uid, mode);
+            int userId = data.readInt();
+            int res = checkUriPermission(uri, pid, uid, mode, userId);
             reply.writeNoException();
             reply.writeInt(res);
             return true;
@@ -1139,7 +1140,8 @@
             String targetPkg = data.readString();
             Uri uri = Uri.CREATOR.createFromParcel(data);
             int mode = data.readInt();
-            grantUriPermission(app, targetPkg, uri, mode);
+            int userId = data.readInt();
+            grantUriPermission(app, targetPkg, uri, mode, userId);
             reply.writeNoException();
             return true;
         }
@@ -1150,7 +1152,8 @@
             IApplicationThread app = ApplicationThreadNative.asInterface(b);
             Uri uri = Uri.CREATOR.createFromParcel(data);
             int mode = data.readInt();
-            revokeUriPermission(app, uri, mode);
+            int userId = data.readInt();
+            revokeUriPermission(app, uri, mode, userId);
             reply.writeNoException();
             return true;
         }
@@ -1159,7 +1162,8 @@
             data.enforceInterface(IActivityManager.descriptor);
             Uri uri = Uri.CREATOR.createFromParcel(data);
             int mode = data.readInt();
-            takePersistableUriPermission(uri, mode);
+            int userId = data.readInt();
+            takePersistableUriPermission(uri, mode, userId);
             reply.writeNoException();
             return true;
         }
@@ -1168,7 +1172,8 @@
             data.enforceInterface(IActivityManager.descriptor);
             Uri uri = Uri.CREATOR.createFromParcel(data);
             int mode = data.readInt();
-            releasePersistableUriPermission(uri, mode);
+            int userId = data.readInt();
+            releasePersistableUriPermission(uri, mode, userId);
             reply.writeNoException();
             return true;
         }
@@ -1542,12 +1547,28 @@
         case CONVERT_TO_TRANSLUCENT_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
-            boolean converted = convertToTranslucent(token);
+            final Bundle bundle;
+            if (data.readInt() == 0) {
+                bundle = null;
+            } else {
+                bundle = data.readBundle();
+            }
+            final ActivityOptions options = bundle == null ? null : new ActivityOptions(bundle);
+            boolean converted = convertToTranslucent(token, options);
             reply.writeNoException();
             reply.writeInt(converted ? 1 : 0);
             return true;
         }
 
+        case GET_ACTIVITY_OPTIONS_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            final ActivityOptions options = getActivityOptions(token);
+            reply.writeNoException();
+            reply.writeBundle(options == null ? null : options.toBundle());
+            return true;
+        }
+
         case SET_IMMERSIVE_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
@@ -1602,7 +1623,8 @@
             String targetPkg = data.readString();
             Uri uri = Uri.CREATOR.createFromParcel(data);
             int mode = data.readInt();
-            grantUriPermissionFromOwner(owner, fromUid, targetPkg, uri, mode);
+            int userId = data.readInt();
+            grantUriPermissionFromOwner(owner, fromUid, targetPkg, uri, mode, userId);
             reply.writeNoException();
             return true;
         }
@@ -1612,10 +1634,11 @@
             IBinder owner = data.readStrongBinder();
             Uri uri = null;
             if (data.readInt() != 0) {
-                Uri.CREATOR.createFromParcel(data);
+                uri = Uri.CREATOR.createFromParcel(data);
             }
             int mode = data.readInt();
-            revokeUriPermissionFromOwner(owner, uri, mode);
+            int userId = data.readInt();
+            revokeUriPermissionFromOwner(owner, uri, mode, userId);
             reply.writeNoException();
             return true;
         }
@@ -1626,7 +1649,8 @@
             String targetPkg = data.readString();
             Uri uri = Uri.CREATOR.createFromParcel(data);
             int modeFlags = data.readInt();
-            int res = checkGrantUriPermission(callingUid, targetPkg, uri, modeFlags);
+            int userId = data.readInt();
+            int res = checkGrantUriPermission(callingUid, targetPkg, uri, modeFlags, userId);
             reply.writeNoException();
             reply.writeInt(res);
             return true;
@@ -3540,7 +3564,7 @@
         reply.recycle();
         return res;
     }
-    public int checkUriPermission(Uri uri, int pid, int uid, int mode) 
+    public int checkUriPermission(Uri uri, int pid, int uid, int mode, int userId)
             throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -3549,6 +3573,7 @@
         data.writeInt(pid);
         data.writeInt(uid);
         data.writeInt(mode);
+        data.writeInt(userId);
         mRemote.transact(CHECK_URI_PERMISSION_TRANSACTION, data, reply, 0);
         reply.readException();
         int res = reply.readInt();
@@ -3557,7 +3582,7 @@
         return res;
     }
     public void grantUriPermission(IApplicationThread caller, String targetPkg,
-            Uri uri, int mode) throws RemoteException {
+            Uri uri, int mode, int userId) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -3565,19 +3590,21 @@
         data.writeString(targetPkg);
         uri.writeToParcel(data, 0);
         data.writeInt(mode);
+        data.writeInt(userId);
         mRemote.transact(GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
     }
     public void revokeUriPermission(IApplicationThread caller, Uri uri,
-            int mode) throws RemoteException {
+            int mode, int userId) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(caller.asBinder());
         uri.writeToParcel(data, 0);
         data.writeInt(mode);
+        data.writeInt(userId);
         mRemote.transact(REVOKE_URI_PERMISSION_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
@@ -3585,12 +3612,14 @@
     }
 
     @Override
-    public void takePersistableUriPermission(Uri uri, int mode) throws RemoteException {
+    public void takePersistableUriPermission(Uri uri, int mode, int userId)
+            throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         uri.writeToParcel(data, 0);
         data.writeInt(mode);
+        data.writeInt(userId);
         mRemote.transact(TAKE_PERSISTABLE_URI_PERMISSION_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
@@ -3598,12 +3627,14 @@
     }
 
     @Override
-    public void releasePersistableUriPermission(Uri uri, int mode) throws RemoteException {
+    public void releasePersistableUriPermission(Uri uri, int mode, int userId)
+            throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         uri.writeToParcel(data, 0);
         data.writeInt(mode);
+        data.writeInt(userId);
         mRemote.transact(RELEASE_PERSISTABLE_URI_PERMISSION_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
@@ -4059,12 +4090,18 @@
         return res;
     }
 
-    public boolean convertToTranslucent(IBinder token)
+    public boolean convertToTranslucent(IBinder token, ActivityOptions options)
             throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
+        if (options == null) {
+            data.writeInt(0);
+        } else {
+            data.writeInt(1);
+            data.writeBundle(options.toBundle());
+        }
         mRemote.transact(CONVERT_TO_TRANSLUCENT_TRANSACTION, data, reply, 0);
         reply.readException();
         boolean res = reply.readInt() != 0;
@@ -4073,6 +4110,20 @@
         return res;
     }
 
+    public ActivityOptions getActivityOptions(IBinder token) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        mRemote.transact(GET_ACTIVITY_OPTIONS_TRANSACTION, data, reply, 0);
+        reply.readException();
+        Bundle bundle = reply.readBundle();
+        ActivityOptions options = bundle == null ? null : new ActivityOptions(bundle);
+        data.recycle();
+        reply.recycle();
+        return options;
+    }
+
     public void setImmersive(IBinder token, boolean immersive)
             throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -4157,7 +4208,7 @@
     }
 
     public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg,
-            Uri uri, int mode) throws RemoteException {
+            Uri uri, int mode, int userId) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -4166,6 +4217,7 @@
         data.writeString(targetPkg);
         uri.writeToParcel(data, 0);
         data.writeInt(mode);
+        data.writeInt(userId);
         mRemote.transact(GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
@@ -4173,7 +4225,7 @@
     }
 
     public void revokeUriPermissionFromOwner(IBinder owner, Uri uri,
-            int mode) throws RemoteException {
+            int mode, int userId) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -4185,6 +4237,7 @@
             data.writeInt(0);
         }
         data.writeInt(mode);
+        data.writeInt(userId);
         mRemote.transact(REVOKE_URI_PERMISSION_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
@@ -4192,7 +4245,7 @@
     }
 
     public int checkGrantUriPermission(int callingUid, String targetPkg,
-            Uri uri, int modeFlags) throws RemoteException {
+            Uri uri, int modeFlags, int userId) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -4200,6 +4253,7 @@
         data.writeString(targetPkg);
         uri.writeToParcel(data, 0);
         data.writeInt(modeFlags);
+        data.writeInt(userId);
         mRemote.transact(CHECK_GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0);
         reply.readException();
         int res = reply.readInt();
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index a49359f..692efd7 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -365,11 +365,15 @@
      * @param listener The listener to use to monitor activity transition events.
      * @return Returns a new ActivityOptions object that you can use to
      *         supply these options as the options Bundle when starting an activity.
+     *         Returns null if the Window does not have {@link Window#FEATURE_CONTENT_TRANSITIONS}.
      * @see android.transition.Transition#setEpicenterCallback(
      *          android.transition.Transition.EpicenterCallback)
      */
     public static ActivityOptions makeSceneTransitionAnimation(Window window,
             ActivityTransitionListener listener) {
+        if (!window.hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
+            return null;
+        }
         ActivityOptions opts = new ActivityOptions();
         opts.mAnimationType = ANIM_SCENE_TRANSITION;
         ExitTransitionCoordinator exit = new ExitTransitionCoordinator(window, listener);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 161cb76..71e4e82 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -47,6 +47,7 @@
 import android.net.IConnectivityManager;
 import android.net.Proxy;
 import android.net.ProxyInfo;
+import android.net.Uri;
 import android.opengl.GLUtils;
 import android.os.AsyncTask;
 import android.os.Binder;
@@ -76,7 +77,6 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.LogPrinter;
-import android.util.Pair;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SuperNotCalledException;
@@ -294,7 +294,6 @@
         boolean isForward;
         int pendingConfigChanges;
         boolean onlyLocalRequest;
-        Bundle activityOptions;
 
         View mPendingRemoveWindow;
         WindowManager mPendingRemoveWindowManager;
@@ -593,8 +592,7 @@
         public final void scheduleResumeActivity(IBinder token, int processState,
                 boolean isForward, Bundle resumeArgs) {
             updateProcessState(processState, false);
-            sendMessage(H.RESUME_ACTIVITY, new Pair<IBinder, Bundle>(token, resumeArgs),
-                    isForward ? 1 : 0);
+            sendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0);
         }
 
         public final void scheduleSendResult(IBinder token, List<ResultInfo> results) {
@@ -611,8 +609,7 @@
                 IVoiceInteractor voiceInteractor, int procState, Bundle state,
                 PersistableBundle persistentState, List<ResultInfo> pendingResults,
                 List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
-                String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler,
-                Bundle resumeArgs) {
+                String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
 
             updateProcessState(procState, false);
 
@@ -636,7 +633,6 @@
             r.profileFile = profileName;
             r.profileFd = profileFd;
             r.autoStopProfiler = autoStopProfiler;
-            r.activityOptions = resumeArgs;
 
             updatePendingConfiguration(curConfig);
 
@@ -839,7 +835,7 @@
             InetAddress.clearDnsCache();
         }
 
-        public void setHttpProxy(String host, String port, String exclList, String pacFileUrl) {
+        public void setHttpProxy(String host, String port, String exclList, Uri pacFileUrl) {
             Proxy.setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
         }
 
@@ -1301,9 +1297,7 @@
                     break;
                 case RESUME_ACTIVITY:
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
-                    final Pair<IBinder, Bundle> resumeArgs = (Pair<IBinder, Bundle>) msg.obj;
-                    handleResumeActivity(resumeArgs.first, resumeArgs.second, true,
-                            msg.arg1 != 0, true);
+                    handleResumeActivity((IBinder) msg.obj, true, msg.arg1 != 0, true);
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
                 case SEND_RESULT:
@@ -2083,7 +2077,7 @@
                     + ", comp=" + name
                     + ", token=" + token);
         }
-        return performLaunchActivity(r, null, null);
+        return performLaunchActivity(r, null);
     }
 
     public final Activity getActivity(IBinder token) {
@@ -2136,8 +2130,7 @@
         sendMessage(H.CLEAN_UP_CONTEXT, cci);
     }
 
-    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent,
-            Bundle options) {
+    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
         // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
 
         ActivityInfo aInfo = r.activityInfo;
@@ -2195,7 +2188,7 @@
                         + r.activityInfo.name + " with config " + config);
                 activity.attach(appContext, this, getInstrumentation(), r.token,
                         r.ident, app, r.intent, r.activityInfo, title, r.parent,
-                        r.embeddedID, r.lastNonConfigurationInstances, config, options,
+                        r.embeddedID, r.lastNonConfigurationInstances, config,
                         r.voiceInteractor);
 
                 if (customIntent != null) {
@@ -2321,12 +2314,12 @@
         if (localLOGV) Slog.v(
             TAG, "Handling launch of " + r);
 
-        Activity a = performLaunchActivity(r, customIntent, r.activityOptions);
+        Activity a = performLaunchActivity(r, customIntent);
 
         if (a != null) {
             r.createdConfig = new Configuration(mConfiguration);
             Bundle oldState = r.state;
-            handleResumeActivity(r.token, r.activityOptions, false, r.isForward,
+            handleResumeActivity(r.token, false, r.isForward,
                     !r.activity.mFinished && !r.startsNotResumed);
 
             if (!r.activity.mFinished && r.startsNotResumed) {
@@ -2886,7 +2879,7 @@
         r.mPendingRemoveWindowManager = null;
     }
 
-    final void handleResumeActivity(IBinder token, Bundle resumeArgs,
+    final void handleResumeActivity(IBinder token,
             boolean clearHide, boolean isForward, boolean reallyResume) {
         // If we are getting ready to gc after going to the background, well
         // we are back active so skip it.
@@ -3809,7 +3802,6 @@
             }
         }
         r.startsNotResumed = tmp.startsNotResumed;
-        r.activityOptions = null;
 
         handleLaunchActivity(r, currentIntent);
     }
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 3eb2fea..ca64788 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -472,7 +472,7 @@
             if (getViewsTransition() != null) {
                 setViewVisibility(mEnteringViews, View.VISIBLE);
             }
-            getDecor().findSharedElements(map);
+            getDecor().findNamedViews(map);
             if (getViewsTransition() != null) {
                 setViewVisibility(mEnteringViews, View.INVISIBLE);
             }
@@ -712,7 +712,7 @@
 
         sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ());
 
-        sharedElementBundle.putString(KEY_NAME, view.getSharedElementName());
+        sharedElementBundle.putString(KEY_NAME, view.getViewName());
 
         Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
         Canvas canvas = new Canvas(bitmap);
@@ -882,7 +882,7 @@
                 imageView.setId(com.android.internal.R.id.shared_element);
                 imageView.setScaleType(ImageView.ScaleType.CENTER);
                 imageView.setImageBitmap(bitmap);
-                imageView.setSharedElementName(name);
+                imageView.setViewName(name);
                 setSharedElementState(imageView, name, state, parentLoc);
                 if (mTargetSharedNames.contains(name)) {
                     accepted.add(imageView);
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index e7902a9..ef4099f 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -25,6 +25,7 @@
 import android.content.pm.ServiceInfo;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Debug;
@@ -151,11 +152,10 @@
             ParcelFileDescriptor profileFd = data.readInt() != 0
                     ? ParcelFileDescriptor.CREATOR.createFromParcel(data) : null;
             boolean autoStopProfiler = data.readInt() != 0;
-            Bundle resumeArgs = data.readBundle();
             scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo,
                     voiceInteractor, procState, state, persistentState,
                     ri, pi, notResumed, isForward, profileName, profileFd,
-                    autoStopProfiler, resumeArgs);
+                    autoStopProfiler);
             return true;
         }
         
@@ -339,7 +339,7 @@
             final String proxy = data.readString();
             final String port = data.readString();
             final String exclList = data.readString();
-            final String pacFileUrl = data.readString();
+            final Uri pacFileUrl = Uri.CREATOR.createFromParcel(data);
             setHttpProxy(proxy, port, exclList, pacFileUrl);
             return true;
         }
@@ -736,8 +736,7 @@
             IVoiceInteractor voiceInteractor, int procState, Bundle state,
             PersistableBundle persistentState, List<ResultInfo> pendingResults,
             List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
-            String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler,
-            Bundle resumeArgs)
+            String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler)
             throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
@@ -763,7 +762,6 @@
             data.writeInt(0);
         }
         data.writeInt(autoStopProfiler ? 1 : 0);
-        data.writeBundle(resumeArgs);
         mRemote.transact(SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
@@ -1008,13 +1006,13 @@
     }
 
     public void setHttpProxy(String proxy, String port, String exclList,
-            String pacFileUrl) throws RemoteException {
+            Uri pacFileUrl) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeString(proxy);
         data.writeString(port);
         data.writeString(exclList);
-        data.writeString(pacFileUrl);
+        pacFileUrl.writeToParcel(data, 0);
         mRemote.transact(SET_HTTP_PROXY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
         data.recycle();
     }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 801182d..b4d8942 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -26,6 +26,7 @@
 import android.bluetooth.BluetoothManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.ContextWrapper;
@@ -1818,8 +1819,8 @@
     public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
          try {
             ActivityManagerNative.getDefault().grantUriPermission(
-                    mMainThread.getApplicationThread(), toPackage, uri,
-                    modeFlags);
+                    mMainThread.getApplicationThread(), toPackage,
+                    ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri));
         } catch (RemoteException e) {
         }
     }
@@ -1828,8 +1829,8 @@
     public void revokeUriPermission(Uri uri, int modeFlags) {
          try {
             ActivityManagerNative.getDefault().revokeUriPermission(
-                    mMainThread.getApplicationThread(), uri,
-                    modeFlags);
+                    mMainThread.getApplicationThread(),
+                    ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri));
         } catch (RemoteException e) {
         }
     }
@@ -1838,12 +1839,17 @@
     public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) {
         try {
             return ActivityManagerNative.getDefault().checkUriPermission(
-                    uri, pid, uid, modeFlags);
+                    ContentProvider.getUriWithoutUserId(uri), pid, uid, modeFlags,
+                    resolveUserId(uri));
         } catch (RemoteException e) {
             return PackageManager.PERMISSION_DENIED;
         }
     }
 
+    private int resolveUserId(Uri uri) {
+        return ContentProvider.getUserIdFromUri(uri, getUserId());
+    }
+
     @Override
     public int checkCallingUriPermission(Uri uri, int modeFlags) {
         int pid = Binder.getCallingPid();
@@ -2280,12 +2286,16 @@
 
         @Override
         protected IContentProvider acquireProvider(Context context, String auth) {
-            return mMainThread.acquireProvider(context, auth, mUser.getIdentifier(), true);
+            return mMainThread.acquireProvider(context,
+                    ContentProvider.getAuthorityWithoutUserId(auth),
+                    resolveUserIdFromAuthority(auth), true);
         }
 
         @Override
         protected IContentProvider acquireExistingProvider(Context context, String auth) {
-            return mMainThread.acquireExistingProvider(context, auth, mUser.getIdentifier(), true);
+            return mMainThread.acquireExistingProvider(context,
+                    ContentProvider.getAuthorityWithoutUserId(auth),
+                    resolveUserIdFromAuthority(auth), true);
         }
 
         @Override
@@ -2295,7 +2305,9 @@
 
         @Override
         protected IContentProvider acquireUnstableProvider(Context c, String auth) {
-            return mMainThread.acquireProvider(c, auth, mUser.getIdentifier(), false);
+            return mMainThread.acquireProvider(c,
+                    ContentProvider.getAuthorityWithoutUserId(auth),
+                    resolveUserIdFromAuthority(auth), false);
         }
 
         @Override
@@ -2312,5 +2324,10 @@
         public void appNotRespondingViaProvider(IContentProvider icp) {
             mMainThread.appNotRespondingViaProvider(icp.asBinder());
         }
+
+        /** @hide */
+        protected int resolveUserIdFromAuthority(String auth) {
+            return ContentProvider.getUserIdFromAuthority(auth, mUser.getIdentifier());
+        }
     }
 }
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index cbb8359..d2d8ed1 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -121,7 +121,7 @@
                             mActivity.convertFromTranslucent();
                         }
                     }
-                });
+                }, null);
                 Drawable background = getDecor().getBackground();
                 if (background != null) {
                     window.setBackgroundDrawable(null);
@@ -230,7 +230,7 @@
                 public void onTranslucentConversionComplete(boolean drawComplete) {
                     fadeOutBackground();
                 }
-            });
+            }, null);
         } else {
             fadeOutBackground();
         }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 074b427..d259b30d 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -210,14 +210,16 @@
     public int checkPermission(String permission, int pid, int uid)
             throws RemoteException;
 
-    public int checkUriPermission(Uri uri, int pid, int uid, int mode)
+    public int checkUriPermission(Uri uri, int pid, int uid, int mode, int userId)
             throws RemoteException;
-    public void grantUriPermission(IApplicationThread caller, String targetPkg,
-            Uri uri, int mode) throws RemoteException;
-    public void revokeUriPermission(IApplicationThread caller, Uri uri,
-            int mode) throws RemoteException;
-    public void takePersistableUriPermission(Uri uri, int modeFlags) throws RemoteException;
-    public void releasePersistableUriPermission(Uri uri, int modeFlags) throws RemoteException;
+    public void grantUriPermission(IApplicationThread caller, String targetPkg, Uri uri,
+            int mode, int userId) throws RemoteException;
+    public void revokeUriPermission(IApplicationThread caller, Uri uri, int mode, int userId)
+            throws RemoteException;
+    public void takePersistableUriPermission(Uri uri, int modeFlags, int userId)
+            throws RemoteException;
+    public void releasePersistableUriPermission(Uri uri, int modeFlags, int userId)
+            throws RemoteException;
     public ParceledListSlice<UriPermission> getPersistedUriPermissions(
             String packageName, boolean incoming) throws RemoteException;
 
@@ -309,8 +311,9 @@
     public void finishHeavyWeightApp() throws RemoteException;
 
     public boolean convertFromTranslucent(IBinder token) throws RemoteException;
-    public boolean convertToTranslucent(IBinder token) throws RemoteException;
+    public boolean convertToTranslucent(IBinder token, ActivityOptions options) throws RemoteException;
     public void notifyActivityDrawn(IBinder token) throws RemoteException;
+    public ActivityOptions getActivityOptions(IBinder token) throws RemoteException;
 
     public void setImmersive(IBinder token, boolean immersive) throws RemoteException;
     public boolean isImmersive(IBinder token) throws RemoteException;
@@ -323,12 +326,12 @@
     
     public IBinder newUriPermissionOwner(String name) throws RemoteException;
     public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg,
-            Uri uri, int mode) throws RemoteException;
+            Uri uri, int mode, int userId) throws RemoteException;
     public void revokeUriPermissionFromOwner(IBinder owner, Uri uri,
-            int mode) throws RemoteException;
+            int mode, int userId) throws RemoteException;
 
-    public int checkGrantUriPermission(int callingUid, String targetPkg,
-            Uri uri, int modeFlags) throws RemoteException;
+    public int checkGrantUriPermission(int callingUid, String targetPkg, Uri uri,
+            int modeFlags, int userId) throws RemoteException;
 
     // Cause the specified process to dump the specified heap.
     public boolean dumpHeap(String process, int userId, boolean managed, String path,
@@ -737,4 +740,5 @@
     int IS_IN_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+216;
     int SET_RECENTS_ACTIVITY_VALUES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+217;
     int START_VOICE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+218;
+    int GET_ACTIVITY_OPTIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+219;
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index a832034..d0df7c3 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -25,6 +25,7 @@
 import android.content.pm.ServiceInfo;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.ParcelFileDescriptor;
@@ -61,8 +62,7 @@
             IVoiceInteractor voiceInteractor, int procState, Bundle state,
             PersistableBundle persistentState, List<ResultInfo> pendingResults,
             List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
-            String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler,
-            Bundle resumeArgs)
+            String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler)
             throws RemoteException;
     void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults,
             List<Intent> pendingNewIntents, int configChanges,
@@ -106,7 +106,7 @@
     void updateTimeZone() throws RemoteException;
     void clearDnsCache() throws RemoteException;
     void setHttpProxy(String proxy, String port, String exclList,
-            String pacFileUrl) throws RemoteException;
+            Uri pacFileUrl) throws RemoteException;
     void processInBackground() throws RemoteException;
     void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args)
             throws RemoteException;
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index bb3e002..b78b9c9 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1036,10 +1036,10 @@
             IllegalAccessException {
         Activity activity = (Activity)clazz.newInstance();
         ActivityThread aThread = null;
-        activity.attach(context, aThread, this, token, application, intent,
+        activity.attach(context, aThread, this, token, 0, application, intent,
                 info, title, parent, id,
                 (Activity.NonConfigurationInstances)lastNonConfigurationInstance,
-                new Configuration());
+                new Configuration(), null);
         return activity;
     }
 
diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java
index 96c7246..9ec7f41 100644
--- a/core/java/android/app/LauncherActivity.java
+++ b/core/java/android/app/LauncherActivity.java
@@ -340,9 +340,11 @@
         super.onCreate(icicle);
         
         mPackageManager = getPackageManager();
-    
-        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-        setProgressBarIndeterminateVisibility(true);
+
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+            requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+            setProgressBarIndeterminateVisibility(true);
+        }
         onSetContentView();
 
         mIconResizer = new IconResizer();
@@ -357,7 +359,9 @@
         updateAlertTitle();
         updateButtonText();
 
-        setProgressBarIndeterminateVisibility(false);
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+            setProgressBarIndeterminateVisibility(false);
+        }
     }
 
     private void updateAlertTitle() {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 58049fd..8884446 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2068,28 +2068,6 @@
     }
 
     /**
-     * Called by a profile owner to disable account management for a specific type of account.
-     *
-     * <p>The calling device admin must be a profile owner. If it is not, a
-     * security exception will be thrown.
-     *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @param accountType For which account management is disabled or enabled.
-     * @param disabled The boolean indicating that account management will be disabled (true) or
-     * enabled (false).
-     */
-    public void setAccountManagementDisabled(ComponentName admin, String accountType,
-            boolean disabled) {
-        if (mService != null) {
-            try {
-                mService.setAccountManagementDisabled(admin, accountType, disabled);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
-            }
-        }
-    }
-
-    /**
      * Called by profile or device owner to re-enable system apps by intent that were disabled
      * by default when the managed profile was created. This should only be called from a profile
      * or device owner running within a managed profile.
@@ -2111,6 +2089,31 @@
     }
 
     /**
+     * Called by a profile owner to disable account management for a specific type of account.
+     *
+     * <p>The calling device admin must be a profile owner. If it is not, a
+     * security exception will be thrown.
+     *
+     * <p>When account management is disabled for an account type, adding or removing an account
+     * of that type will not be possible.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param accountType For which account management is disabled or enabled.
+     * @param disabled The boolean indicating that account management will be disabled (true) or
+     * enabled (false).
+     */
+    public void setAccountManagementDisabled(ComponentName admin, String accountType,
+            boolean disabled) {
+        if (mService != null) {
+            try {
+                mService.setAccountManagementDisabled(admin, accountType, disabled);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
      * Gets the array of accounts for which account management is disabled by the profile owner.
      *
      * <p> Account management can be disabled/enabled by calling
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index 50c4fed..b44abf9 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import static android.content.ContentProvider.maybeAddUserId;
 import android.content.res.AssetFileDescriptor;
 import android.graphics.Bitmap;
 import android.net.Uri;
@@ -186,7 +187,7 @@
         final CharSequence mText;
         final String mHtmlText;
         final Intent mIntent;
-        final Uri mUri;
+        Uri mUri;
 
         /**
          * Create an Item consisting of a single block of (possibly styled) text.
@@ -809,6 +810,24 @@
         }
     }
 
+    /**
+     * Prepare this {@link ClipData} to leave an app process.
+     *
+     * @hide
+     */
+    public void prepareToLeaveUser(int userId) {
+        final int size = mItems.size();
+        for (int i = 0; i < size; i++) {
+            final Item item = mItems.get(i);
+            if (item.mIntent != null) {
+                item.mIntent.prepareToLeaveUser(userId);
+            }
+            if (item.mUri != null) {
+                item.mUri = maybeAddUserId(item.mUri, userId);
+            }
+        }
+    }
+
     @Override
     public String toString() {
         StringBuilder b = new StringBuilder(128);
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 02c850b..be9782f 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -36,6 +36,7 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.util.Log;
+import android.text.TextUtils;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -195,6 +196,7 @@
                 return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,
                         CancellationSignal.fromTransport(cancellationSignal));
             }
+            uri = getUriWithoutUserId(uri);
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.query(
@@ -207,6 +209,7 @@
 
         @Override
         public String getType(Uri uri) {
+            uri = getUriWithoutUserId(uri);
             return ContentProvider.this.getType(uri);
         }
 
@@ -215,9 +218,11 @@
             if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
                 return rejectInsert(uri, initialValues);
             }
+            int userId = getUserIdFromUri(uri);
+            uri = getUriWithoutUserId(uri);
             final String original = setCallingPackage(callingPkg);
             try {
-                return ContentProvider.this.insert(uri, initialValues);
+                return maybeAddUserId(ContentProvider.this.insert(uri, initialValues), userId);
             } finally {
                 setCallingPackage(original);
             }
@@ -228,6 +233,7 @@
             if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
                 return 0;
             }
+            uri = getUriWithoutUserId(uri);
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.bulkInsert(uri, initialValues);
@@ -240,24 +246,39 @@
         public ContentProviderResult[] applyBatch(String callingPkg,
                 ArrayList<ContentProviderOperation> operations)
                 throws OperationApplicationException {
-            for (ContentProviderOperation operation : operations) {
+            int numOperations = operations.size();
+            final int[] userIds = new int[numOperations];
+            for (int i = 0; i < numOperations; i++) {
+                ContentProviderOperation operation = operations.get(i);
+                userIds[i] = getUserIdFromUri(operation.getUri());
                 if (operation.isReadOperation()) {
                     if (enforceReadPermission(callingPkg, operation.getUri())
                             != AppOpsManager.MODE_ALLOWED) {
                         throw new OperationApplicationException("App op not allowed", 0);
                     }
                 }
-
                 if (operation.isWriteOperation()) {
                     if (enforceWritePermission(callingPkg, operation.getUri())
                             != AppOpsManager.MODE_ALLOWED) {
                         throw new OperationApplicationException("App op not allowed", 0);
                     }
                 }
+                if (userIds[i] != UserHandle.USER_CURRENT) {
+                    // Removing the user id from the uri.
+                    operation = new ContentProviderOperation(operation, true);
+                }
+                operations.set(i, operation);
             }
             final String original = setCallingPackage(callingPkg);
             try {
-                return ContentProvider.this.applyBatch(operations);
+                ContentProviderResult[] results = ContentProvider.this.applyBatch(operations);
+                for (int i = 0; i < results.length ; i++) {
+                    if (userIds[i] != UserHandle.USER_CURRENT) {
+                        // Adding the userId to the uri.
+                        results[i] = new ContentProviderResult(results[i], userIds[i]);
+                    }
+                }
+                return results;
             } finally {
                 setCallingPackage(original);
             }
@@ -268,6 +289,7 @@
             if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
                 return 0;
             }
+            uri = getUriWithoutUserId(uri);
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.delete(uri, selection, selectionArgs);
@@ -282,6 +304,7 @@
             if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
                 return 0;
             }
+            uri = getUriWithoutUserId(uri);
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.update(uri, values, selection, selectionArgs);
@@ -295,6 +318,7 @@
                 String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
                 throws FileNotFoundException {
             enforceFilePermission(callingPkg, uri, mode);
+            uri = getUriWithoutUserId(uri);
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.openFile(
@@ -309,6 +333,7 @@
                 String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
                 throws FileNotFoundException {
             enforceFilePermission(callingPkg, uri, mode);
+            uri = getUriWithoutUserId(uri);
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.openAssetFile(
@@ -330,6 +355,7 @@
 
         @Override
         public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
+            uri = getUriWithoutUserId(uri);
             return ContentProvider.this.getStreamTypes(uri, mimeTypeFilter);
         }
 
@@ -337,6 +363,7 @@
         public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri uri, String mimeType,
                 Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException {
             enforceFilePermission(callingPkg, uri, "r");
+            uri = getUriWithoutUserId(uri);
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.openTypedAssetFile(
@@ -356,9 +383,11 @@
             if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
                 return null;
             }
+            int userId = getUserIdFromUri(uri);
+            uri = getUriWithoutUserId(uri);
             final String original = setCallingPackage(callingPkg);
             try {
-                return ContentProvider.this.canonicalize(uri);
+                return maybeAddUserId(ContentProvider.this.canonicalize(uri), userId);
             } finally {
                 setCallingPackage(original);
             }
@@ -369,9 +398,11 @@
             if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
                 return null;
             }
+            int userId = getUserIdFromUri(uri);
+            uri = getUriWithoutUserId(uri);
             final String original = setCallingPackage(callingPkg);
             try {
-                return ContentProvider.this.uncanonicalize(uri);
+                return maybeAddUserId(ContentProvider.this.uncanonicalize(uri), userId);
             } finally {
                 setCallingPackage(original);
             }
@@ -1680,4 +1711,75 @@
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         writer.println("nothing to dump");
     }
+
+    /** @hide */
+    public static int getUserIdFromAuthority(String auth, int defaultUserId) {
+        if (auth == null) return defaultUserId;
+        int end = auth.indexOf('@');
+        if (end == -1) return defaultUserId;
+        String userIdString = auth.substring(0, end);
+        try {
+            return Integer.parseInt(userIdString);
+        } catch (NumberFormatException e) {
+            Log.w(TAG, "Error parsing userId.", e);
+            return UserHandle.USER_NULL;
+        }
+    }
+
+    /** @hide */
+    public static int getUserIdFromAuthority(String auth) {
+        return getUserIdFromAuthority(auth, UserHandle.USER_CURRENT);
+    }
+
+    /** @hide */
+    public static int getUserIdFromUri(Uri uri, int defaultUserId) {
+        if (uri == null) return defaultUserId;
+        return getUserIdFromAuthority(uri.getAuthority(), defaultUserId);
+    }
+
+    /** @hide */
+    public static int getUserIdFromUri(Uri uri) {
+        return getUserIdFromUri(uri, UserHandle.USER_CURRENT);
+    }
+
+    /**
+     * Removes userId part from authority string. Expects format:
+     * userId@some.authority
+     * If there is no userId in the authority, it symply returns the argument
+     * @hide
+     */
+    public static String getAuthorityWithoutUserId(String auth) {
+        if (auth == null) return null;
+        int end = auth.indexOf('@');
+        return auth.substring(end+1);
+    }
+
+    /** @hide */
+    public static Uri getUriWithoutUserId(Uri uri) {
+        if (uri == null) return null;
+        Uri.Builder builder = uri.buildUpon();
+        builder.authority(getAuthorityWithoutUserId(uri.getAuthority()));
+        return builder.build();
+    }
+
+    /** @hide */
+    public static boolean uriHasUserId(Uri uri) {
+        if (uri == null) return false;
+        return !TextUtils.isEmpty(uri.getUserInfo());
+    }
+
+    /** @hide */
+    public static Uri maybeAddUserId(Uri uri, int userId) {
+        if (uri == null) return null;
+        if (userId != UserHandle.USER_CURRENT
+                && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+            if (!uriHasUserId(uri)) {
+                //We don't add the user Id if there's already one
+                Uri.Builder builder = uri.buildUpon();
+                builder.encodedAuthority("" + userId + "@" + uri.getEncodedAuthority());
+                return builder.build();
+            }
+        }
+        return uri;
+    }
 }
diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java
index 12e9bab..136e54d 100644
--- a/core/java/android/content/ContentProviderOperation.java
+++ b/core/java/android/content/ContentProviderOperation.java
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import android.content.ContentProvider;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Parcel;
@@ -87,6 +88,31 @@
         mYieldAllowed = source.readInt() != 0;
     }
 
+    /** @hide */
+    public ContentProviderOperation(ContentProviderOperation cpo, boolean removeUserIdFromUri) {
+        mType = cpo.mType;
+        if (removeUserIdFromUri) {
+            mUri = ContentProvider.getUriWithoutUserId(cpo.mUri);
+        } else {
+            mUri = cpo.mUri;
+        }
+        mValues = cpo.mValues;
+        mSelection = cpo.mSelection;
+        mSelectionArgs = cpo.mSelectionArgs;
+        mExpectedCount = cpo.mExpectedCount;
+        mSelectionArgsBackReferences = cpo.mSelectionArgsBackReferences;
+        mValuesBackReferences = cpo.mValuesBackReferences;
+        mYieldAllowed = cpo.mYieldAllowed;
+    }
+
+    /** @hide */
+    public ContentProviderOperation getWithoutUserIdInUri() {
+        if (ContentProvider.uriHasUserId(mUri)) {
+            return new ContentProviderOperation(this, true);
+        }
+        return this;
+    }
+
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mType);
         Uri.writeToParcel(dest, mUri);
@@ -387,7 +413,6 @@
         }
     };
 
-
     /**
      * Used to add parameters to a {@link ContentProviderOperation}. The {@link Builder} is
      * first created by calling {@link ContentProviderOperation#newInsert(android.net.Uri)},
diff --git a/core/java/android/content/ContentProviderResult.java b/core/java/android/content/ContentProviderResult.java
index 5d188ef..ec3d002 100644
--- a/core/java/android/content/ContentProviderResult.java
+++ b/core/java/android/content/ContentProviderResult.java
@@ -16,7 +16,9 @@
 
 package android.content;
 
+import android.content.ContentProvider;
 import android.net.Uri;
+import android.os.UserHandle;
 import android.os.Parcelable;
 import android.os.Parcel;
 
@@ -50,6 +52,12 @@
         }
     }
 
+    /** @hide */
+    public ContentProviderResult(ContentProviderResult cpr, int userId) {
+        uri = ContentProvider.maybeAddUserId(cpr.uri, userId);
+        count = cpr.count;
+    }
+
     public void writeToParcel(Parcel dest, int flags) {
         if (uri == null) {
             dest.writeInt(1);
@@ -81,4 +89,4 @@
         }
         return "ContentProviderResult(count=" + count + ")";
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 5b41394..7642e13 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1643,7 +1643,8 @@
      */
     public void takePersistableUriPermission(Uri uri, @Intent.AccessUriMode int modeFlags) {
         try {
-            ActivityManagerNative.getDefault().takePersistableUriPermission(uri, modeFlags);
+            ActivityManagerNative.getDefault().takePersistableUriPermission(
+                    ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri));
         } catch (RemoteException e) {
         }
     }
@@ -1658,7 +1659,8 @@
      */
     public void releasePersistableUriPermission(Uri uri, @Intent.AccessUriMode int modeFlags) {
         try {
-            ActivityManagerNative.getDefault().releasePersistableUriPermission(uri, modeFlags);
+            ActivityManagerNative.getDefault().releasePersistableUriPermission(
+                    ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri));
         } catch (RemoteException e) {
         }
     }
@@ -2462,4 +2464,9 @@
     private final Context mContext;
     final String mPackageName;
     private static final String TAG = "ContentResolver";
+
+    /** @hide */
+    public int resolveUserId(Uri uri) {
+        return ContentProvider.getUserIdFromUri(uri, mContext.getUserId());
+    }
 }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3cfc56c..076f657 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -26,6 +26,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.pm.ActivityInfo;
+import static android.content.ContentProvider.maybeAddUserId;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
@@ -7433,6 +7434,41 @@
     }
 
     /**
+     * Prepare this {@link Intent} to be sent to another user
+     *
+     * @hide
+     */
+    public void prepareToLeaveUser(int userId) {
+        Uri data = getData();
+        if (data != null) {
+            mData = maybeAddUserId(data, userId);
+        }
+        if (mSelector != null) {
+            mSelector.prepareToLeaveUser(userId);
+        }
+        if (mClipData != null) {
+            mClipData.prepareToLeaveUser(userId);
+        }
+        String action = getAction();
+        if (ACTION_SEND.equals(action)) {
+            final Uri stream = getParcelableExtra(EXTRA_STREAM);
+            if (stream != null) {
+                putExtra(EXTRA_STREAM, maybeAddUserId(stream, userId));
+            }
+        }
+        if (ACTION_SEND_MULTIPLE.equals(action)) {
+            final ArrayList<Uri> streams = getParcelableArrayListExtra(EXTRA_STREAM);
+            if (streams != null) {
+                ArrayList<Uri> newStreams = new ArrayList<Uri>();
+                for (int i = 0; i < streams.size(); i++) {
+                    newStreams.add(maybeAddUserId(streams.get(i), userId));
+                }
+                putParcelableArrayListExtra(EXTRA_STREAM, newStreams);
+            }
+        }
+    }
+
+    /**
      * Migrate any {@link #EXTRA_STREAM} in {@link #ACTION_SEND} and
      * {@link #ACTION_SEND_MULTIPLE} to {@link ClipData}. Also inspects nested
      * intents in {@link #ACTION_CHOOSER}.
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 6e38a22f..6659278 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -17,6 +17,7 @@
 package android.hardware.camera2;
 
 import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.utils.TypeReference;
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
@@ -126,11 +127,13 @@
         return keyList;
     }
 
+    // TODO: make final or abstract
     public static class Key<T> {
 
         private boolean mHasTag;
         private int mTag;
         private final Class<T> mType;
+        private final TypeReference<T> mTypeReference;
         private final String mName;
 
         /**
@@ -144,6 +147,22 @@
             }
             mName = name;
             mType = type;
+            mTypeReference = TypeReference.createSpecializedTypeReference(type);
+        }
+
+        /**
+         * @hide
+         */
+        @SuppressWarnings("unchecked")
+        public Key(String name, TypeReference<T> typeReference) {
+            if (name == null) {
+                throw new NullPointerException("Key needs a valid name");
+            } else if (typeReference == null) {
+                throw new NullPointerException("TypeReference needs to be non-null");
+            }
+            mName = name;
+            mType = (Class<T>)typeReference.getRawType();
+            mTypeReference = typeReference;
         }
 
         public final String getName() {
@@ -152,11 +171,10 @@
 
         @Override
         public final int hashCode() {
-            return mName.hashCode();
+            return mName.hashCode() ^ mTypeReference.hashCode();
         }
 
         @Override
-        @SuppressWarnings("unchecked")
         public final boolean equals(Object o) {
             if (this == o) {
                 return true;
@@ -166,9 +184,8 @@
                 return false;
             }
 
-            Key lhs = (Key) o;
-
-            return mName.equals(lhs.mName) && mType.equals(lhs.mType);
+            Key<?> lhs = (Key<?>)o;
+            return mName.equals(lhs.mName) && mTypeReference.equals(lhs.mTypeReference);
         }
 
         /**
@@ -192,11 +209,29 @@
         }
 
         /**
+         * Get the raw class backing the type {@code T} for this key.
+         *
+         * <p>The distinction is only important if {@code T} is a generic, e.g.
+         * {@code Range<Integer>} since the nested type will be erased.</p>
+         *
          * @hide
          */
         public final Class<T> getType() {
+            // TODO: remove this; other places should use #getTypeReference() instead
             return mType;
         }
+
+        /**
+         * Get the type reference backing the type {@code T} for this key.
+         *
+         * <p>The distinction is only important if {@code T} is a generic, e.g.
+         * {@code Range<Integer>} since the nested type will be retained.</p>
+         *
+         * @hide
+         */
+        public final TypeReference<T> getTypeReference() {
+            return mTypeReference;
+        }
     }
 
     /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
diff --git a/core/java/android/hardware/camera2/ColorSpaceTransform.java b/core/java/android/hardware/camera2/ColorSpaceTransform.java
index 9912e4b..5e4c0a2 100644
--- a/core/java/android/hardware/camera2/ColorSpaceTransform.java
+++ b/core/java/android/hardware/camera2/ColorSpaceTransform.java
@@ -17,7 +17,8 @@
 package android.hardware.camera2;
 
 import static com.android.internal.util.Preconditions.*;
-import android.hardware.camera2.impl.HashCodeHelpers;
+
+import android.hardware.camera2.utils.HashCodeHelpers;
 
 import java.util.Arrays;
 
diff --git a/core/java/android/hardware/camera2/LensShadingMap.java b/core/java/android/hardware/camera2/LensShadingMap.java
index 2c224f64..2b0108c 100644
--- a/core/java/android/hardware/camera2/LensShadingMap.java
+++ b/core/java/android/hardware/camera2/LensShadingMap.java
@@ -19,7 +19,7 @@
 import static com.android.internal.util.Preconditions.*;
 import static android.hardware.camera2.RggbChannelVector.*;
 
-import android.hardware.camera2.impl.HashCodeHelpers;
+import android.hardware.camera2.utils.HashCodeHelpers;
 
 import java.util.Arrays;
 
diff --git a/core/java/android/hardware/camera2/MeteringRectangle.java b/core/java/android/hardware/camera2/MeteringRectangle.java
index ff7a745..bb8e5b1 100644
--- a/core/java/android/hardware/camera2/MeteringRectangle.java
+++ b/core/java/android/hardware/camera2/MeteringRectangle.java
@@ -20,7 +20,7 @@
 
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.hardware.camera2.impl.HashCodeHelpers;
+import android.hardware.camera2.utils.HashCodeHelpers;
 
 /**
  * An immutable class to represent a rectangle {@code (x,y, width, height)} with an
@@ -186,10 +186,7 @@
      */
     @Override
     public boolean equals(final Object other) {
-        if (other instanceof MeteringRectangle) {
-            return equals(other);
-        }
-        return false;
+        return other instanceof MeteringRectangle && equals((MeteringRectangle)other);
     }
 
     /**
diff --git a/core/java/android/hardware/camera2/Rational.java b/core/java/android/hardware/camera2/Rational.java
index 77b8c26..693ee2b 100644
--- a/core/java/android/hardware/camera2/Rational.java
+++ b/core/java/android/hardware/camera2/Rational.java
@@ -91,14 +91,14 @@
      * <p>A reduced form of a Rational is calculated by dividing both the numerator and the
      * denominator by their greatest common divisor.</p>
      *
-     * <pre>
+     * <pre>{@code
      *      (new Rational(1, 2)).equals(new Rational(1, 2)) == true   // trivially true
      *      (new Rational(2, 3)).equals(new Rational(1, 2)) == false  // trivially false
      *      (new Rational(1, 2)).equals(new Rational(2, 4)) == true   // true after reduction
      *      (new Rational(0, 0)).equals(new Rational(0, 0)) == true   // NaN.equals(NaN)
      *      (new Rational(1, 0)).equals(new Rational(5, 0)) == true   // both are +infinity
      *      (new Rational(1, 0)).equals(new Rational(-1, 0)) == false // +infinity != -infinity
-     * </pre>
+     * }</pre>
      *
      * @param obj a reference to another object
      *
@@ -159,16 +159,15 @@
         return (float) mNumerator / (float) mDenominator;
     }
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public int hashCode() {
-        final long INT_MASK = 0xffffffffL;
+        // Bias the hash code for the first (2^16) values for both numerator and denominator
+        int numeratorFlipped = mNumerator << 16 | mNumerator >>> 16;
 
-        long asLong = INT_MASK & mNumerator;
-        asLong <<= 32;
-
-        asLong |= (INT_MASK & mDenominator);
-
-        return ((Long)asLong).hashCode();
+        return mDenominator ^ numeratorFlipped;
     }
 
     /**
diff --git a/core/java/android/hardware/camera2/ReprocessFormatsMap.java b/core/java/android/hardware/camera2/ReprocessFormatsMap.java
index c6c59d4..894a499 100644
--- a/core/java/android/hardware/camera2/ReprocessFormatsMap.java
+++ b/core/java/android/hardware/camera2/ReprocessFormatsMap.java
@@ -18,7 +18,7 @@
 
 import static com.android.internal.util.Preconditions.*;
 
-import android.hardware.camera2.impl.HashCodeHelpers;
+import android.hardware.camera2.utils.HashCodeHelpers;
 
 import java.util.Arrays;
 
diff --git a/core/java/android/hardware/camera2/StreamConfiguration.java b/core/java/android/hardware/camera2/StreamConfiguration.java
index c53dd7c..a514034 100644
--- a/core/java/android/hardware/camera2/StreamConfiguration.java
+++ b/core/java/android/hardware/camera2/StreamConfiguration.java
@@ -20,7 +20,7 @@
 import static android.hardware.camera2.StreamConfigurationMap.checkArgumentFormatInternal;
 
 import android.graphics.ImageFormat;
-import android.hardware.camera2.impl.HashCodeHelpers;
+import android.hardware.camera2.utils.HashCodeHelpers;
 import android.util.Size;
 
 /**
@@ -57,7 +57,7 @@
             final int format, final int width, final int height, final boolean input) {
         mFormat = checkArgumentFormatInternal(format);
         mWidth = checkArgumentPositive(width, "width must be positive");
-        mHeight = checkArgumentPositive(width, "height must be positive");
+        mHeight = checkArgumentPositive(height, "height must be positive");
         mInput = input;
     }
 
diff --git a/core/java/android/hardware/camera2/StreamConfigurationDuration.java b/core/java/android/hardware/camera2/StreamConfigurationDuration.java
index 189ae62..6a31156 100644
--- a/core/java/android/hardware/camera2/StreamConfigurationDuration.java
+++ b/core/java/android/hardware/camera2/StreamConfigurationDuration.java
@@ -20,7 +20,7 @@
 import static android.hardware.camera2.StreamConfigurationMap.checkArgumentFormatInternal;
 
 import android.graphics.ImageFormat;
-import android.hardware.camera2.impl.HashCodeHelpers;
+import android.hardware.camera2.utils.HashCodeHelpers;
 import android.util.Size;
 
 /**
@@ -54,7 +54,7 @@
             final int format, final int width, final int height, final long durationNs) {
         mFormat =  checkArgumentFormatInternal(format);
         mWidth = checkArgumentPositive(width, "width must be positive");
-        mHeight = checkArgumentPositive(width, "height must be positive");
+        mHeight = checkArgumentPositive(height, "height must be positive");
         mDurationNs = checkArgumentNonnegative(durationNs, "durationNs must be non-negative");
     }
 
diff --git a/core/java/android/hardware/camera2/StreamConfigurationMap.java b/core/java/android/hardware/camera2/StreamConfigurationMap.java
index e24fd1b..5ddd7d6 100644
--- a/core/java/android/hardware/camera2/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/StreamConfigurationMap.java
@@ -18,7 +18,7 @@
 
 import android.graphics.ImageFormat;
 import android.graphics.PixelFormat;
-import android.hardware.camera2.impl.HashCodeHelpers;
+import android.hardware.camera2.utils.HashCodeHelpers;
 import android.view.Surface;
 import android.util.Size;
 
diff --git a/core/java/android/hardware/camera2/TonemapCurve.java b/core/java/android/hardware/camera2/TonemapCurve.java
index ee20d68..2958ebf 100644
--- a/core/java/android/hardware/camera2/TonemapCurve.java
+++ b/core/java/android/hardware/camera2/TonemapCurve.java
@@ -19,7 +19,7 @@
 import static com.android.internal.util.Preconditions.*;
 
 import android.graphics.PointF;
-import android.hardware.camera2.impl.HashCodeHelpers;
+import android.hardware.camera2.utils.HashCodeHelpers;
 
 import java.util.Arrays;
 
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index c5e5753..9a06e97 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -23,16 +23,33 @@
 import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.Face;
-import android.hardware.camera2.Rational;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.marshal.MarshalRegistry;
+import android.hardware.camera2.marshal.impl.MarshalQueryableArray;
+import android.hardware.camera2.marshal.impl.MarshalQueryableBoolean;
+import android.hardware.camera2.marshal.impl.MarshalQueryableColorSpaceTransform;
+import android.hardware.camera2.marshal.impl.MarshalQueryableEnum;
+import android.hardware.camera2.marshal.impl.MarshalQueryableMeteringRectangle;
+import android.hardware.camera2.marshal.impl.MarshalQueryableNativeByteToInteger;
+import android.hardware.camera2.marshal.impl.MarshalQueryableParcelable;
+import android.hardware.camera2.marshal.impl.MarshalQueryablePrimitive;
+import android.hardware.camera2.marshal.impl.MarshalQueryableRange;
+import android.hardware.camera2.marshal.impl.MarshalQueryableRect;
+import android.hardware.camera2.marshal.impl.MarshalQueryableReprocessFormatsMap;
+import android.hardware.camera2.marshal.impl.MarshalQueryableRggbChannelVector;
+import android.hardware.camera2.marshal.impl.MarshalQueryableSize;
+import android.hardware.camera2.marshal.impl.MarshalQueryableSizeF;
+import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfiguration;
+import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfigurationDuration;
+import android.hardware.camera2.marshal.impl.MarshalQueryableString;
 import android.os.Parcelable;
 import android.os.Parcel;
 import android.util.Log;
 
-import java.lang.reflect.Array;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
-import java.util.HashMap;
 
 /**
  * Implementation of camera metadata marshal/unmarshal across Binder to
@@ -89,7 +106,6 @@
         nativeWriteToParcel(dest);
     }
 
-    @SuppressWarnings("unchecked")
     @Override
     public <T> T get(Key<T> key) {
         T value = getOverride(key);
@@ -169,275 +185,6 @@
         mMetadataPtr = 0; // set it to 0 again to prevent eclipse from making this field final
     }
 
-    private static int getTypeSize(int nativeType) {
-        switch(nativeType) {
-            case TYPE_BYTE:
-                return 1;
-            case TYPE_INT32:
-            case TYPE_FLOAT:
-                return 4;
-            case TYPE_INT64:
-            case TYPE_DOUBLE:
-            case TYPE_RATIONAL:
-                return 8;
-        }
-
-        throw new UnsupportedOperationException("Unknown type, can't get size "
-                + nativeType);
-    }
-
-    private static Class<?> getExpectedType(int nativeType) {
-        switch(nativeType) {
-            case TYPE_BYTE:
-                return Byte.TYPE;
-            case TYPE_INT32:
-                return Integer.TYPE;
-            case TYPE_FLOAT:
-                return Float.TYPE;
-            case TYPE_INT64:
-                return Long.TYPE;
-            case TYPE_DOUBLE:
-                return Double.TYPE;
-            case TYPE_RATIONAL:
-                return Rational.class;
-        }
-
-        throw new UnsupportedOperationException("Unknown type, can't map to Java type "
-                + nativeType);
-    }
-
-    @SuppressWarnings("unchecked")
-    private static <T> int packSingleNative(T value, ByteBuffer buffer, Class<T> type,
-            int nativeType, boolean sizeOnly) {
-
-        if (!sizeOnly) {
-            /**
-             * Rewrite types when the native type doesn't match the managed type
-             *  - Boolean -> Byte
-             *  - Integer -> Byte
-             */
-
-            if (nativeType == TYPE_BYTE && type == Boolean.TYPE) {
-                // Since a boolean can't be cast to byte, and we don't want to use putBoolean
-                boolean asBool = (Boolean) value;
-                byte asByte = (byte) (asBool ? 1 : 0);
-                value = (T) (Byte) asByte;
-            } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) {
-                int asInt = (Integer) value;
-                byte asByte = (byte) asInt;
-                value = (T) (Byte) asByte;
-            } else if (type != getExpectedType(nativeType)) {
-                throw new UnsupportedOperationException("Tried to pack a type of " + type +
-                        " but we expected the type to be " + getExpectedType(nativeType));
-            }
-
-            if (nativeType == TYPE_BYTE) {
-                buffer.put((Byte) value);
-            } else if (nativeType == TYPE_INT32) {
-                buffer.putInt((Integer) value);
-            } else if (nativeType == TYPE_FLOAT) {
-                buffer.putFloat((Float) value);
-            } else if (nativeType == TYPE_INT64) {
-                buffer.putLong((Long) value);
-            } else if (nativeType == TYPE_DOUBLE) {
-                buffer.putDouble((Double) value);
-            } else if (nativeType == TYPE_RATIONAL) {
-                Rational r = (Rational) value;
-                buffer.putInt(r.getNumerator());
-                buffer.putInt(r.getDenominator());
-            }
-
-        }
-
-        return getTypeSize(nativeType);
-    }
-
-    @SuppressWarnings({"unchecked", "rawtypes"})
-    private static <T> int packSingle(T value, ByteBuffer buffer, Class<T> type, int nativeType,
-            boolean sizeOnly) {
-
-        int size = 0;
-
-        if (type.isPrimitive() || type == Rational.class) {
-            size = packSingleNative(value, buffer, type, nativeType, sizeOnly);
-        } else if (type.isEnum()) {
-            size = packEnum((Enum)value, buffer, (Class<Enum>)type, nativeType, sizeOnly);
-        } else if (type.isArray()) {
-            size = packArray(value, buffer, type, nativeType, sizeOnly);
-        } else {
-            size = packClass(value, buffer, type, nativeType, sizeOnly);
-        }
-
-        return size;
-    }
-
-    private static <T extends Enum<T>> int packEnum(T value, ByteBuffer buffer, Class<T> type,
-            int nativeType, boolean sizeOnly) {
-
-        // TODO: add support for enums with their own values.
-        return packSingleNative(getEnumValue(value), buffer, Integer.TYPE, nativeType, sizeOnly);
-    }
-
-    @SuppressWarnings("unchecked")
-    private static <T> int packClass(T value, ByteBuffer buffer, Class<T> type, int nativeType,
-            boolean sizeOnly) {
-
-        MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType);
-        if (marshaler == null) {
-            throw new IllegalArgumentException(String.format("Unknown Key type: %s", type));
-        }
-
-        return marshaler.marshal(value, buffer, nativeType, sizeOnly);
-    }
-
-    private static <T> int packArray(T value, ByteBuffer buffer, Class<T> type, int nativeType,
-            boolean sizeOnly) {
-
-        int size = 0;
-        int arrayLength = Array.getLength(value);
-
-        @SuppressWarnings("unchecked")
-        Class<Object> componentType = (Class<Object>)type.getComponentType();
-
-        for (int i = 0; i < arrayLength; ++i) {
-            size += packSingle(Array.get(value, i), buffer, componentType, nativeType, sizeOnly);
-        }
-
-        return size;
-    }
-
-    @SuppressWarnings("unchecked")
-    private static <T> T unpackSingleNative(ByteBuffer buffer, Class<T> type, int nativeType) {
-
-        T val;
-
-        if (nativeType == TYPE_BYTE) {
-            val = (T) (Byte) buffer.get();
-        } else if (nativeType == TYPE_INT32) {
-            val = (T) (Integer) buffer.getInt();
-        } else if (nativeType == TYPE_FLOAT) {
-            val = (T) (Float) buffer.getFloat();
-        } else if (nativeType == TYPE_INT64) {
-            val = (T) (Long) buffer.getLong();
-        } else if (nativeType == TYPE_DOUBLE) {
-            val = (T) (Double) buffer.getDouble();
-        } else if (nativeType == TYPE_RATIONAL) {
-            val = (T) new Rational(buffer.getInt(), buffer.getInt());
-        } else {
-            throw new UnsupportedOperationException("Unknown type, can't unpack a native type "
-                + nativeType);
-        }
-
-        /**
-         * Rewrite types when the native type doesn't match the managed type
-         *  - Byte -> Boolean
-         *  - Byte -> Integer
-         */
-
-        if (nativeType == TYPE_BYTE && type == Boolean.TYPE) {
-            // Since a boolean can't be cast to byte, and we don't want to use getBoolean
-            byte asByte = (Byte) val;
-            boolean asBool = asByte != 0;
-            val = (T) (Boolean) asBool;
-        } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) {
-            byte asByte = (Byte) val;
-            int asInt = asByte;
-            val = (T) (Integer) asInt;
-        } else if (type != getExpectedType(nativeType)) {
-            throw new UnsupportedOperationException("Tried to unpack a type of " + type +
-                    " but we expected the type to be " + getExpectedType(nativeType));
-        }
-
-        return val;
-    }
-
-    @SuppressWarnings({"unchecked", "rawtypes"})
-    private static <T> T unpackSingle(ByteBuffer buffer, Class<T> type, int nativeType) {
-
-        if (type.isPrimitive() || type == Rational.class) {
-            return unpackSingleNative(buffer, type, nativeType);
-        }
-
-        if (type.isEnum()) {
-            return (T) unpackEnum(buffer, (Class<Enum>)type, nativeType);
-        }
-
-        if (type.isArray()) {
-            return unpackArray(buffer, type, nativeType);
-        }
-
-        T instance = unpackClass(buffer, type, nativeType);
-
-        return instance;
-    }
-
-    private static <T extends Enum<T>> T unpackEnum(ByteBuffer buffer, Class<T> type,
-            int nativeType) {
-        int ordinal = unpackSingleNative(buffer, Integer.TYPE, nativeType);
-        return getEnumFromValue(type, ordinal);
-    }
-
-    private static <T> T unpackClass(ByteBuffer buffer, Class<T> type, int nativeType) {
-
-        MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType);
-        if (marshaler == null) {
-            throw new IllegalArgumentException("Unknown class type: " + type);
-        }
-
-        return marshaler.unmarshal(buffer, nativeType);
-    }
-
-    @SuppressWarnings("unchecked")
-    private static <T> T unpackArray(ByteBuffer buffer, Class<T> type, int nativeType) {
-
-        Class<?> componentType = type.getComponentType();
-        Object array;
-
-        int elementSize = getTypeSize(nativeType);
-
-        MetadataMarshalClass<?> marshaler = getMarshaler(componentType, nativeType);
-        if (marshaler != null) {
-            elementSize = marshaler.getNativeSize(nativeType);
-        }
-
-        if (elementSize != MetadataMarshalClass.NATIVE_SIZE_DYNAMIC) {
-            int remaining = buffer.remaining();
-            int arraySize = remaining / elementSize;
-
-            if (VERBOSE) {
-                Log.v(TAG,
-                        String.format(
-                            "Attempting to unpack array (count = %d, element size = %d, bytes " +
-                            "remaining = %d) for type %s",
-                            arraySize, elementSize, remaining, type));
-            }
-
-            array = Array.newInstance(componentType, arraySize);
-            for (int i = 0; i < arraySize; ++i) {
-               Object elem = unpackSingle(buffer, componentType, nativeType);
-               Array.set(array, i, elem);
-            }
-        } else {
-            // Dynamic size, use an array list.
-            ArrayList<Object> arrayList = new ArrayList<Object>();
-
-            int primitiveSize = getTypeSize(nativeType);
-            while (buffer.remaining() >= primitiveSize) {
-                Object elem = unpackSingle(buffer, componentType, nativeType);
-                arrayList.add(elem);
-            }
-
-            array = arrayList.toArray((T[]) Array.newInstance(componentType, 0));
-        }
-
-        if (buffer.remaining() != 0) {
-            Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking "
-                    + type);
-        }
-
-        return (T) array;
-    }
-
     private <T> T getBase(Key<T> key) {
         int tag = key.getTag();
         byte[] values = readValues(tag);
@@ -445,10 +192,9 @@
             return null;
         }
 
-        int nativeType = getNativeType(tag);
-
+        Marshaler<T> marshaler = getMarshalerForKey(key);
         ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
-        return unpackSingle(buffer, key.getType(), nativeType);
+        return marshaler.unmarshal(buffer);
     }
 
     // Need overwrite some metadata that has different definitions between native
@@ -632,19 +378,19 @@
         int tag = key.getTag();
 
         if (value == null) {
-            writeValues(tag, null);
+            // Erase the entry
+            writeValues(tag, /*src*/null);
             return;
-        }
+        } // else update the entry to a new value
 
-        int nativeType = getNativeType(tag);
-
-        int size = packSingle(value, null, key.getType(), nativeType, /* sizeOnly */true);
+        Marshaler<T> marshaler = getMarshalerForKey(key);
+        int size = marshaler.calculateMarshalSize(value);
 
         // TODO: Optimization. Cache the byte[] and reuse if the size is big enough.
         byte[] values = new byte[size];
 
         ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
-        packSingle(value, buffer, key.getType(), nativeType, /*sizeOnly*/false);
+        marshaler.marshal(value, buffer);
 
         writeValues(tag, values);
     }
@@ -870,125 +616,64 @@
         }
     }
 
-    private static final HashMap<Class<? extends Enum>, int[]> sEnumValues =
-            new HashMap<Class<? extends Enum>, int[]>();
     /**
-     * Register a non-sequential set of values to be used with the pack/unpack functions.
-     * This enables get/set to correctly marshal the enum into a value that is C-compatible.
+     * Get the marshaler compatible with the {@code key} and type {@code T}.
      *
-     * @param enumType The class for an enum
-     * @param values A list of values mapping to the ordinals of the enum
-     *
-     * @hide
+     * @throws UnsupportedOperationException
+     *          if the native/managed type combination for {@code key} is not supported
      */
-    public static <T extends Enum<T>> void registerEnumValues(Class<T> enumType, int[] values) {
-        if (enumType.getEnumConstants().length != values.length) {
-            throw new IllegalArgumentException(
-                    "Expected values array to be the same size as the enumTypes values "
-                            + values.length + " for type " + enumType);
-        }
-        if (VERBOSE) {
-            Log.v(TAG, "Registered enum values for type " + enumType + " values");
-        }
-
-        sEnumValues.put(enumType, values);
+    private static <T> Marshaler<T> getMarshalerForKey(Key<T> key) {
+        return MarshalRegistry.getMarshaler(key.getTypeReference(),
+                getNativeType(key.getTag()));
     }
 
-    /**
-     * Get the numeric value from an enum. This is usually the same as the ordinal value for
-     * enums that have fully sequential values, although for C-style enums the range of values
-     * may not map 1:1.
-     *
-     * @param enumValue Enum instance
-     * @return Int guaranteed to be ABI-compatible with the C enum equivalent
-     */
-    private static <T extends Enum<T>> int getEnumValue(T enumValue) {
-        int[] values;
-        values = sEnumValues.get(enumValue.getClass());
-
-        int ordinal = enumValue.ordinal();
-        if (values != null) {
-            return values[ordinal];
-        }
-
-        return ordinal;
-    }
-
-    /**
-     * Finds the enum corresponding to it's numeric value. Opposite of {@link #getEnumValue} method.
-     *
-     * @param enumType Class of the enum we want to find
-     * @param value The numeric value of the enum
-     * @return An instance of the enum
-     */
-    private static <T extends Enum<T>> T getEnumFromValue(Class<T> enumType, int value) {
-        int ordinal;
-
-        int[] registeredValues = sEnumValues.get(enumType);
-        if (registeredValues != null) {
-            ordinal = -1;
-
-            for (int i = 0; i < registeredValues.length; ++i) {
-                if (registeredValues[i] == value) {
-                    ordinal = i;
-                    break;
-                }
-            }
-        } else {
-            ordinal = value;
-        }
-
-        T[] values = enumType.getEnumConstants();
-
-        if (ordinal < 0 || ordinal >= values.length) {
-            throw new IllegalArgumentException(
-                    String.format(
-                            "Argument 'value' (%d) was not a valid enum value for type %s "
-                                    + "(registered? %b)",
-                            value,
-                            enumType, (registeredValues != null)));
-        }
-
-        return values[ordinal];
-    }
-
-    static HashMap<Class<?>, MetadataMarshalClass<?>> sMarshalerMap = new
-            HashMap<Class<?>, MetadataMarshalClass<?>>();
-
-    private static <T> void registerMarshaler(MetadataMarshalClass<T> marshaler) {
-        sMarshalerMap.put(marshaler.getMarshalingClass(), marshaler);
-    }
-
-    @SuppressWarnings("unchecked")
-    private static <T> MetadataMarshalClass<T> getMarshaler(Class<T> type, int nativeType) {
-        MetadataMarshalClass<T> marshaler = (MetadataMarshalClass<T>) sMarshalerMap.get(type);
-
-        if (marshaler != null && !marshaler.isNativeTypeSupported(nativeType)) {
-            throw new UnsupportedOperationException("Unsupported type " + nativeType +
-                    " to be marshalled to/from a " + type);
-        }
-
-        return marshaler;
-    }
-
-    /**
-     * We use a class initializer to allow the native code to cache some field offsets
-     */
-    static {
-        nativeClassInit();
-
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private static void registerAllMarshalers() {
         if (VERBOSE) {
             Log.v(TAG, "Shall register metadata marshalers");
         }
 
-        // load built-in marshallers
-        registerMarshaler(new MetadataMarshalRect());
-        registerMarshaler(new MetadataMarshalSize());
-        registerMarshaler(new MetadataMarshalString());
+        MarshalQueryable[] queryList = new MarshalQueryable[] {
+                // marshalers for standard types
+                new MarshalQueryablePrimitive(),
+                new MarshalQueryableEnum(),
+                new MarshalQueryableArray(),
 
+                // pseudo standard types, that expand/narrow the native type into a managed type
+                new MarshalQueryableBoolean(),
+                new MarshalQueryableNativeByteToInteger(),
+
+                // marshalers for custom types
+                new MarshalQueryableRect(),
+                new MarshalQueryableSize(),
+                new MarshalQueryableSizeF(),
+                new MarshalQueryableString(),
+                new MarshalQueryableReprocessFormatsMap(),
+                new MarshalQueryableRange(),
+                new MarshalQueryableMeteringRectangle(),
+                new MarshalQueryableColorSpaceTransform(),
+                new MarshalQueryableStreamConfiguration(),
+                new MarshalQueryableStreamConfigurationDuration(),
+                new MarshalQueryableRggbChannelVector(),
+
+                // generic parcelable marshaler (MUST BE LAST since it has lowest priority)
+                new MarshalQueryableParcelable(),
+        };
+
+        for (MarshalQueryable query : queryList) {
+            MarshalRegistry.registerMarshalQueryable(query);
+        }
         if (VERBOSE) {
             Log.v(TAG, "Registered metadata marshalers");
         }
     }
 
+    static {
+        /*
+         * We use a class initializer to allow the native code to cache some field offsets
+         */
+        nativeClassInit();
+        registerAllMarshalers();
+    }
+
 }
diff --git a/core/java/android/hardware/camera2/impl/MetadataMarshalClass.java b/core/java/android/hardware/camera2/impl/MetadataMarshalClass.java
deleted file mode 100644
index 6d224ef..0000000
--- a/core/java/android/hardware/camera2/impl/MetadataMarshalClass.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2013 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.hardware.camera2.impl;
-
-import java.nio.ByteBuffer;
-
-public interface MetadataMarshalClass<T> {
-
-    /**
-     * Marshal the specified object instance (value) into a byte buffer.
-     *
-     * @param value the value of type T that we wish to write into the byte buffer
-     * @param buffer the byte buffer into which the marshalled object will be written
-     * @param nativeType the native type, e.g.
-     *        {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}.
-     *        Guaranteed to be one for which isNativeTypeSupported returns true.
-     * @param sizeOnly if this is true, don't write to the byte buffer. calculate the size only.
-     * @return the size that needs to be written to the byte buffer
-     */
-    int marshal(T value, ByteBuffer buffer, int nativeType, boolean sizeOnly);
-
-    /**
-     * Unmarshal a new object instance from the byte buffer.
-     * @param buffer the byte buffer, from which we will read the object
-     * @param nativeType the native type, e.g.
-     *        {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}.
-     *        Guaranteed to be one for which isNativeTypeSupported returns true.
-     * @return a new instance of type T read from the byte buffer
-     */
-    T unmarshal(ByteBuffer buffer, int nativeType);
-
-    Class<T> getMarshalingClass();
-
-    /**
-     * Determines whether or not this marshaller supports this native type. Most marshallers
-     * will are likely to only support one type.
-     *
-     * @param nativeType the native type, e.g.
-     *        {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}
-     * @return true if it supports, false otherwise
-     */
-    boolean isNativeTypeSupported(int nativeType);
-
-    public static int NATIVE_SIZE_DYNAMIC = -1;
-
-    /**
-     * How many bytes T will take up if marshalled to/from nativeType
-     * @param nativeType the native type, e.g.
-     *        {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}
-     * @return a size in bytes, or NATIVE_SIZE_DYNAMIC if the size is dynamic
-     */
-    int getNativeSize(int nativeType);
-}
diff --git a/core/java/android/hardware/camera2/impl/MetadataMarshalRect.java b/core/java/android/hardware/camera2/impl/MetadataMarshalRect.java
deleted file mode 100644
index ab72c4f..0000000
--- a/core/java/android/hardware/camera2/impl/MetadataMarshalRect.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2013 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.hardware.camera2.impl;
-
-import android.graphics.Rect;
-
-import java.nio.ByteBuffer;
-
-public class MetadataMarshalRect implements MetadataMarshalClass<Rect> {
-    private static final int SIZE = 16;
-
-    @Override
-    public int marshal(Rect value, ByteBuffer buffer, int nativeType, boolean sizeOnly) {
-        if (sizeOnly) {
-            return SIZE;
-        }
-
-        buffer.putInt(value.left);
-        buffer.putInt(value.top);
-        buffer.putInt(value.width());
-        buffer.putInt(value.height());
-
-        return SIZE;
-    }
-
-    @Override
-    public Rect unmarshal(ByteBuffer buffer, int nativeType) {
-
-        int left = buffer.getInt();
-        int top = buffer.getInt();
-        int width = buffer.getInt();
-        int height = buffer.getInt();
-
-        int right = left + width;
-        int bottom = top + height;
-
-        return new Rect(left, top, right, bottom);
-    }
-
-    @Override
-    public Class<Rect> getMarshalingClass() {
-        return Rect.class;
-    }
-
-    @Override
-    public boolean isNativeTypeSupported(int nativeType) {
-        return nativeType == CameraMetadataNative.TYPE_INT32;
-    }
-
-    @Override
-    public int getNativeSize(int nativeType) {
-        return SIZE;
-    }
-}
diff --git a/core/java/android/hardware/camera2/impl/MetadataMarshalSize.java b/core/java/android/hardware/camera2/impl/MetadataMarshalSize.java
deleted file mode 100644
index e8143e0..0000000
--- a/core/java/android/hardware/camera2/impl/MetadataMarshalSize.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2013 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.hardware.camera2.impl;
-
-import android.hardware.camera2.Size;
-
-import java.nio.ByteBuffer;
-
-public class MetadataMarshalSize implements MetadataMarshalClass<Size> {
-
-    private static final int SIZE = 8;
-
-    @Override
-    public int marshal(Size value, ByteBuffer buffer, int nativeType, boolean sizeOnly) {
-        if (sizeOnly) {
-            return SIZE;
-        }
-
-        buffer.putInt(value.getWidth());
-        buffer.putInt(value.getHeight());
-
-        return SIZE;
-    }
-
-    @Override
-    public Size unmarshal(ByteBuffer buffer, int nativeType) {
-        int width = buffer.getInt();
-        int height = buffer.getInt();
-
-        return new Size(width, height);
-    }
-
-    @Override
-    public Class<Size> getMarshalingClass() {
-        return Size.class;
-    }
-
-    @Override
-    public boolean isNativeTypeSupported(int nativeType) {
-        return nativeType == CameraMetadataNative.TYPE_INT32;
-    }
-
-    @Override
-    public int getNativeSize(int nativeType) {
-        return SIZE;
-    }
-}
diff --git a/core/java/android/hardware/camera2/impl/MetadataMarshalString.java b/core/java/android/hardware/camera2/impl/MetadataMarshalString.java
deleted file mode 100644
index b61b8d3..0000000
--- a/core/java/android/hardware/camera2/impl/MetadataMarshalString.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2013 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.hardware.camera2.impl;
-
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-
-public class MetadataMarshalString implements MetadataMarshalClass<String> {
-
-    private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
-
-    @Override
-    public int marshal(String value, ByteBuffer buffer, int nativeType, boolean sizeOnly) {
-        byte[] arr = value.getBytes(UTF8_CHARSET);
-
-        if (!sizeOnly) {
-            buffer.put(arr);
-            buffer.put((byte)0); // metadata strings are NULL-terminated
-        }
-
-        return arr.length + 1;
-    }
-
-    @Override
-    public String unmarshal(ByteBuffer buffer, int nativeType) {
-
-        buffer.mark(); // save the current position
-
-        boolean foundNull = false;
-        int stringLength = 0;
-        while (buffer.hasRemaining()) {
-            if (buffer.get() == (byte)0) {
-                foundNull = true;
-                break;
-            }
-
-            stringLength++;
-        }
-        if (!foundNull) {
-            throw new IllegalArgumentException("Strings must be null-terminated");
-        }
-
-        buffer.reset(); // go back to the previously marked position
-
-        byte[] strBytes = new byte[stringLength + 1];
-        buffer.get(strBytes, /*dstOffset*/0, stringLength + 1); // including null character
-
-        // not including null character
-        return new String(strBytes, /*offset*/0, stringLength, UTF8_CHARSET);
-    }
-
-    @Override
-    public Class<String> getMarshalingClass() {
-        return String.class;
-    }
-
-    @Override
-    public boolean isNativeTypeSupported(int nativeType) {
-        return nativeType == CameraMetadataNative.TYPE_BYTE;
-    }
-
-    @Override
-    public int getNativeSize(int nativeType) {
-        return NATIVE_SIZE_DYNAMIC;
-    }
-}
diff --git a/core/java/android/hardware/camera2/marshal/MarshalHelpers.java b/core/java/android/hardware/camera2/marshal/MarshalHelpers.java
new file mode 100644
index 0000000..fd72ee2
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/MarshalHelpers.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static com.android.internal.util.Preconditions.*;
+
+import android.hardware.camera2.Rational;
+import android.hardware.camera2.impl.CameraMetadataNative;
+
+/**
+ * Static functions in order to help implementing various marshaler functionality.
+ *
+ * <p>The intention is to statically import everything from this file into another file when
+ * implementing a new marshaler (or marshal queryable).</p>
+ *
+ * <p>The helpers are centered around providing primitive knowledge of the native types,
+ * such as the native size, the managed class wrappers, and various precondition checks.</p>
+ */
+public final class MarshalHelpers {
+
+    public static final int SIZEOF_BYTE = 1;
+    public static final int SIZEOF_INT32 = Integer.SIZE / Byte.SIZE;
+    public static final int SIZEOF_INT64 = Long.SIZE / Byte.SIZE;
+    public static final int SIZEOF_FLOAT = Float.SIZE / Byte.SIZE;
+    public static final int SIZEOF_DOUBLE = Double.SIZE / Byte.SIZE;
+    public static final int SIZEOF_RATIONAL = SIZEOF_INT32 * 2;
+
+    /**
+     * Get the size in bytes for the native camera metadata type.
+     *
+     * <p>This used to determine how many bytes it would take to encode/decode a single value
+     * of that {@link nativeType}.</p>
+     *
+     * @param nativeType the native type, e.g.
+     *        {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}.
+     * @return size in bytes >= 1
+     *
+     * @throws UnsupportedOperationException if nativeType was not one of the built-in types
+     */
+    public static int getPrimitiveTypeSize(int nativeType) {
+        switch (nativeType) {
+            case TYPE_BYTE:
+                return SIZEOF_BYTE;
+            case TYPE_INT32:
+                return SIZEOF_INT32;
+            case TYPE_FLOAT:
+                return SIZEOF_FLOAT;
+            case TYPE_INT64:
+                return SIZEOF_INT64;
+            case TYPE_DOUBLE:
+                return SIZEOF_DOUBLE;
+            case TYPE_RATIONAL:
+                return SIZEOF_RATIONAL;
+        }
+
+        throw new UnsupportedOperationException("Unknown type, can't get size for "
+                + nativeType);
+    }
+
+
+    /**
+     * Ensure that the {@code klass} is one of the metadata-primitive classes.
+     *
+     * @param klass a non-{@code null} reference
+     * @return {@code klass} instance
+     *
+     * @throws UnsupportedOperationException if klass was not one of the built-in classes
+     * @throws NullPointerException if klass was null
+     *
+     * @see #isPrimitiveClass
+     */
+    public static <T> Class<T> checkPrimitiveClass(Class<T> klass) {
+        checkNotNull(klass, "klass must not be null");
+
+        if (isPrimitiveClass(klass)) {
+            return klass;
+        }
+
+        throw new UnsupportedOperationException("Unsupported class '" + klass +
+                "'; expected a metadata primitive class");
+    }
+
+    /**
+     * Checks whether or not {@code klass} is one of the metadata-primitive classes.
+     *
+     * <p>The following types (whether boxed or unboxed) are considered primitive:
+     * <ul>
+     * <li>byte
+     * <li>int
+     * <li>float
+     * <li>double
+     * <li>Rational
+     * </ul>
+     * </p>
+     *
+     * <p>This doesn't strictly follow the java understanding of primitive since
+     * boxed objects are included, Rational is included, and other types such as char and
+     * short are not included.</p>
+     *
+     * @param klass a {@link Class} instance; using {@code null} will return {@code false}
+     * @return {@code true} if primitive, {@code false} otherwise
+     */
+    public static <T> boolean isPrimitiveClass(Class<T> klass) {
+        if (klass == null) {
+            return false;
+        }
+
+        if (klass == byte.class || klass == Byte.class) {
+            return true;
+        } else if (klass == int.class || klass == Integer.class) {
+            return true;
+        } else if (klass == float.class || klass == Float.class) {
+            return true;
+        } else if (klass == long.class || klass == Long.class) {
+            return true;
+        } else if (klass == double.class || klass == Double.class) {
+            return true;
+        } else if (klass == Rational.class) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Wrap {@code klass} with its wrapper variant if it was a {@code Class} corresponding
+     * to a Java primitive.
+     *
+     * <p>Non-primitive classes are passed through as-is.</p>
+     *
+     * <p>For example, for a primitive {@code int.class => Integer.class},
+     * but for a non-primitive {@code Rational.class => Rational.class}.</p>
+     *
+     * @param klass a {@code Class} reference
+     *
+     * @return wrapped class object, or same class object if non-primitive
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> Class<T> wrapClassIfPrimitive(Class<T> klass) {
+        if (klass == byte.class) {
+            return (Class<T>)Byte.class;
+        } else if (klass == int.class) {
+            return (Class<T>)Integer.class;
+        } else if (klass == float.class) {
+            return (Class<T>)Float.class;
+        } else if (klass == long.class) {
+            return (Class<T>)Long.class;
+        } else if (klass == double.class) {
+            return (Class<T>)Double.class;
+        }
+
+        return klass;
+    }
+
+    /**
+     * Return a human-readable representation of the {@code nativeType}, e.g. "TYPE_INT32"
+     *
+     * <p>Out-of-range values return a string with "UNKNOWN" as the prefix.</p>
+     *
+     * @param nativeType the native type
+     *
+     * @return human readable type name
+     */
+    public static String toStringNativeType(int nativeType) {
+        switch (nativeType) {
+            case TYPE_BYTE:
+                return "TYPE_BYTE";
+            case TYPE_INT32:
+                return "TYPE_INT32";
+            case TYPE_FLOAT:
+                return "TYPE_FLOAT";
+            case TYPE_INT64:
+                return "TYPE_INT64";
+            case TYPE_DOUBLE:
+                return "TYPE_DOUBLE";
+            case TYPE_RATIONAL:
+                return "TYPE_RATIONAL";
+        }
+
+        return "UNKNOWN(" + nativeType + ")";
+    }
+
+    /**
+     * Ensure that the {@code nativeType} is one of the native types supported
+     * by {@link CameraMetadataNative}.
+     *
+     * @param nativeType the native type
+     *
+     * @return the native type
+     *
+     * @throws UnsupportedOperationException if the native type was invalid
+     */
+    public static int checkNativeType(int nativeType) {
+        switch (nativeType) {
+            case TYPE_BYTE:
+            case TYPE_INT32:
+            case TYPE_FLOAT:
+            case TYPE_INT64:
+            case TYPE_DOUBLE:
+            case TYPE_RATIONAL:
+                return nativeType;
+        }
+
+        throw new UnsupportedOperationException("Unknown nativeType " + nativeType);
+    }
+
+    /**
+     * Ensure that the expected and actual native types are equal.
+     *
+     * @param expectedNativeType the expected native type
+     * @param actualNativeType the actual native type
+     * @return the actual native type
+     *
+     * @throws UnsupportedOperationException if the types are not equal
+     */
+    public static int checkNativeTypeEquals(int expectedNativeType, int actualNativeType) {
+        if (expectedNativeType != actualNativeType) {
+            throw new UnsupportedOperationException(
+                    String.format("Expected native type %d, but got %d",
+                            expectedNativeType, actualNativeType));
+        }
+
+        return actualNativeType;
+    }
+
+    private MarshalHelpers() {
+        throw new AssertionError();
+    }
+}
diff --git a/core/java/android/hardware/camera2/marshal/MarshalQueryable.java b/core/java/android/hardware/camera2/marshal/MarshalQueryable.java
new file mode 100644
index 0000000..35fed1f
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/MarshalQueryable.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal;
+
+import android.hardware.camera2.utils.TypeReference;
+
+/**
+ * Query if a marshaler can marshal to/from a particular native and managed type; if it supports
+ * the combination, allow creating a marshaler instance to do the serialization.
+ *
+ * <p>Not all queryable instances will support exactly one combination. Some, such as the
+ * primitive queryable will support all primitive to/from managed mappings (as long as they are
+ * 1:1). Others, such as the rectangle queryable will only support integer to rectangle mappings.
+ * </p>
+ *
+ * <p>Yet some others are codependent on other queryables; e.g. array queryables might only support
+ * a type map for {@code T[]} if another queryable exists with support for the component type
+ * {@code T}.</p>
+ */
+public interface MarshalQueryable<T> {
+    /**
+     * Create a marshaler between the selected managed and native type.
+     *
+     * <p>This marshaler instance is only good for that specific type mapping; and will refuse
+     * to map other managed types, other native types, or an other combination that isn't
+     * this exact one.</p>
+     *
+     * @param managedType a managed type reference
+     * @param nativeType the native type, e.g.
+     *          {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}
+     * @return
+     *
+     * @throws UnsupportedOperationException
+     *          if {@link #isTypeMappingSupported} returns {@code false}
+     */
+    public Marshaler<T> createMarshaler(
+            TypeReference<T> managedType, int nativeType);
+
+    /**
+     * Determine whether or not this query marshal is able to create a marshaler that will
+     * support the managed type and native type mapping.
+     *
+     * <p>If this returns {@code true}, then a marshaler can be instantiated by
+     * {@link #createMarshaler} that will marshal data to/from the native type
+     * from/to the managed type.</p>
+     *
+     * <p>Most marshalers are likely to only support one type map.</p>
+     */
+    public boolean isTypeMappingSupported(TypeReference<T> managedType, int nativeType);
+}
diff --git a/core/java/android/hardware/camera2/marshal/MarshalRegistry.java b/core/java/android/hardware/camera2/marshal/MarshalRegistry.java
new file mode 100644
index 0000000..92d9057
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/MarshalRegistry.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal;
+
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.utils.TypeReference;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Registry of supported marshalers; add new query-able marshalers or lookup existing ones.</p>
+ */
+public class MarshalRegistry {
+
+    /**
+     * Register a marshal queryable for the managed type {@code T}.
+     *
+     * <p>Multiple marshal queryables for the same managed type {@code T} may be registered;
+     * this is desirable if they support different native types (e.g. marshaler 1 supports
+     * {@code Integer <-> TYPE_INT32}, marshaler 2 supports {@code Integer <-> TYPE_BYTE}.</p>
+     *
+     * @param queryable a non-{@code null} marshal queryable that supports marshaling {@code T}
+     */
+    public static <T> void registerMarshalQueryable(MarshalQueryable<T> queryable) {
+        sRegisteredMarshalQueryables.add(queryable);
+    }
+
+    /**
+     * Lookup a marshaler between {@code T} and {@code nativeType}.
+     *
+     * <p>Marshalers are looked up in the order they were registered; earlier registered
+     * marshal queriers get priority.</p>
+     *
+     * @param typeToken The compile-time type reference for {@code T}
+     * @param nativeType The native type, e.g. {@link CameraMetadataNative#TYPE_BYTE TYPE_BYTE}
+     * @return marshaler a non-{@code null} marshaler that supports marshaling the type combo
+     *
+     * @throws UnsupportedOperationException If no marshaler matching the args could be found
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> Marshaler<T> getMarshaler(TypeReference<T> typeToken, int nativeType) {
+        // TODO: can avoid making a new token each time by code-genning
+        // the list of type tokens and native types from the keys (at the call sites)
+        MarshalToken<T> marshalToken = new MarshalToken<T>(typeToken, nativeType);
+
+        /*
+         * Marshalers are instantiated lazily once they are looked up; successive lookups
+         * will not instantiate new marshalers.
+         */
+        Marshaler<T> marshaler =
+                (Marshaler<T>) sMarshalerMap.get(marshalToken);
+
+        if (sRegisteredMarshalQueryables.size() == 0) {
+            throw new AssertionError("No available query marshalers registered");
+        }
+
+        if (marshaler == null) {
+            // Query each marshaler to see if they support the native/managed type combination
+            for (MarshalQueryable<?> potentialMarshaler : sRegisteredMarshalQueryables) {
+
+                MarshalQueryable<T> castedPotential =
+                        (MarshalQueryable<T>)potentialMarshaler;
+
+                if (castedPotential.isTypeMappingSupported(typeToken, nativeType)) {
+                    marshaler = castedPotential.createMarshaler(typeToken, nativeType);
+                    break;
+                }
+            }
+        }
+
+        if (marshaler == null) {
+            throw new UnsupportedOperationException(
+                     "Could not find marshaler that matches the requested " +
+                     "combination of type reference " +
+                     typeToken + " and native type " +
+                     MarshalHelpers.toStringNativeType(nativeType));
+        }
+
+        sMarshalerMap.put(marshalToken, marshaler);
+
+        return marshaler;
+    }
+
+    private static class MarshalToken<T> {
+        public MarshalToken(TypeReference<T> typeReference, int nativeType) {
+            this.typeReference = typeReference;
+            this.nativeType = nativeType;
+        }
+
+        final TypeReference<T> typeReference;
+        final int nativeType;
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof MarshalToken<?>) {
+                MarshalToken<?> otherToken = (MarshalToken<?>)other;
+                return typeReference.equals(otherToken.typeReference) &&
+                        nativeType == otherToken.nativeType;
+            }
+
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return typeReference.hashCode() ^ nativeType;
+        }
+    }
+
+    private static List<MarshalQueryable<?>> sRegisteredMarshalQueryables =
+            new ArrayList<MarshalQueryable<?>>();
+    private static HashMap<MarshalToken<?>, Marshaler<?>> sMarshalerMap =
+            new HashMap<MarshalToken<?>, Marshaler<?>>();
+
+    private MarshalRegistry() {
+        throw new AssertionError();
+    }
+}
diff --git a/core/java/android/hardware/camera2/marshal/Marshaler.java b/core/java/android/hardware/camera2/marshal/Marshaler.java
new file mode 100644
index 0000000..eb0ad15
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/Marshaler.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal;
+
+import android.hardware.camera2.utils.TypeReference;
+
+import java.nio.ByteBuffer;
+
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Base class to marshal data to/from managed/native metadata byte buffers.
+ *
+ * <p>This class should not be created directly; an instance of it can be obtained
+ * using {@link MarshalQueryable#createMarshaler} for the same type {@code T} if the native type
+ * mapping for {@code T} {@link MarshalQueryable#isTypeMappingSupported supported}.</p>
+ *
+ * @param <T> the compile-time managed type
+ */
+public abstract class Marshaler<T> {
+
+    protected final TypeReference<T> mTypeReference;
+    protected final int mNativeType;
+
+    /**
+     * Instantiate a marshaler between a single managed/native type combination.
+     *
+     * <p>This particular managed/native type combination must be supported by
+     * {@link #isTypeMappingSupported}.</p>
+     *
+     * @param query an instance of {@link MarshalQueryable}
+     * @param typeReference the managed type reference
+     *        Must be one for which {@link #isTypeMappingSupported} returns {@code true}
+     * @param nativeType the native type, e.g.
+     *        {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}.
+     *        Must be one for which {@link #isTypeMappingSupported} returns {@code true}.
+     *
+     * @throws NullPointerException if any args were {@code null}
+     * @throws UnsupportedOperationException if the type mapping was not supported
+     */
+    protected Marshaler(
+            MarshalQueryable<T> query, TypeReference<T> typeReference, int nativeType) {
+        mTypeReference = checkNotNull(typeReference, "typeReference must not be null");
+        mNativeType = checkNativeType(nativeType);
+
+        if (!query.isTypeMappingSupported(typeReference, nativeType)) {
+            throw new UnsupportedOperationException(
+                    "Unsupported type marshaling for managed type "
+                            + typeReference + " and native type "
+                            + MarshalHelpers.toStringNativeType(nativeType));
+        }
+    }
+
+    /**
+     * Marshal the specified object instance (value) into a byte buffer.
+     *
+     * <p>Upon completion, the {@link ByteBuffer#position()} will have advanced by
+     * the {@link #calculateMarshalSize marshal size} of {@code value}.</p>
+     *
+     * @param value the value of type T that we wish to write into the byte buffer
+     * @param buffer the byte buffer into which the marshaled object will be written
+     */
+    public abstract void marshal(T value, ByteBuffer buffer);
+
+    /**
+     * Get the size in bytes for how much space would be required to write this {@code value}
+     * into a byte buffer using the given {@code nativeType}.
+     *
+     * <p>If the size of this {@code T} instance when serialized into a buffer is always constant,
+     * then this method will always return the same value (and particularly, it will return
+     * an equivalent value to {@link #getNativeSize()}.</p>
+     *
+     * <p>Overriding this method is a must when the size is {@link NATIVE_SIZE_DYNAMIC dynamic}.</p>
+     *
+     * @param value the value of type T that we wish to write into the byte buffer
+     * @return the size that would need to be written to the byte buffer
+     */
+    public int calculateMarshalSize(T value) {
+        int nativeSize = getNativeSize();
+
+        if (nativeSize == NATIVE_SIZE_DYNAMIC) {
+            throw new AssertionError("Override this function for dynamically-sized objects");
+        }
+
+        return nativeSize;
+    }
+
+    /**
+     * Unmarshal a new object instance from the byte buffer into its managed type.
+     *
+     * <p>Upon completion, the {@link ByteBuffer#position()} will have advanced by
+     * the {@link #calculateMarshalSize marshal size} of the returned {@code T} instance.</p>
+     *
+     * @param buffer the byte buffer, from which we will read the object
+     * @return a new instance of type T read from the byte buffer
+     */
+    public abstract T unmarshal(ByteBuffer buffer);
+
+    /**
+     * Used to denote variable-length data structures.
+     *
+     * <p>If the size is dynamic then we can't know ahead of time how big of a data structure
+     * to preallocate for e.g. arrays, so one object must be unmarshaled at a time.</p>
+     */
+    public static int NATIVE_SIZE_DYNAMIC = -1;
+
+    /**
+     * How many bytes a single instance of {@code T} will take up if marshalled to/from
+     * {@code nativeType}.
+     *
+     * <p>When unmarshaling data from native to managed, the instance {@code T} is not yet
+     * available. If the native size is always a fixed mapping regardless of the instance of
+     * {@code T} (e.g. if the type is not a container of some sort), it can be used to preallocate
+     * containers for {@code T} to avoid resizing them.</p>
+     *
+     * <p>In particular, the array marshaler takes advantage of this (when size is not dynamic)
+     * to preallocate arrays of the right length when unmarshaling an array {@code T[]}.</p>
+     *
+     * @return a size in bytes, or {@link #NATIVE_SIZE_DYNAMIC} if the size is dynamic
+     */
+    public abstract int getNativeSize();
+
+    /**
+     * The type reference for {@code T} for the managed type side of this marshaler.
+     */
+    public TypeReference<T> getTypeReference() {
+        return mTypeReference;
+    }
+
+    /** The native type corresponding to this marshaler for the native side of this marshaler.*/
+    public int getNativeType() {
+        return mNativeType;
+    }
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableArray.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableArray.java
new file mode 100644
index 0000000..22b87ef
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableArray.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal.impl;
+
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.marshal.MarshalRegistry;
+import android.hardware.camera2.utils.TypeReference;
+import android.util.Log;
+
+import java.lang.reflect.Array;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+/**
+ * Marshal any array {@code T}.
+ *
+ * <p>To marshal any {@code T} to/from a native type, the marshaler for T to/from that native type
+ * also has to exist.</p>
+ *
+ * <p>{@code T} can be either a T2[] where T2 is an object type, or a P[] where P is a
+ * built-in primitive (e.g. int[], float[], etc).</p>
+
+ * @param <T> the type of the array (e.g. T = int[], or T = Rational[])
+ */
+public class MarshalQueryableArray<T> implements MarshalQueryable<T> {
+
+    private static final String TAG = MarshalQueryableArray.class.getSimpleName();
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private class MarshalerArray extends Marshaler<T> {
+        private final Class<T> mClass;
+        private final Marshaler<?> mComponentMarshaler;
+        private final Class<?> mComponentClass;
+
+        @SuppressWarnings("unchecked")
+        protected MarshalerArray(TypeReference<T> typeReference, int nativeType) {
+            super(MarshalQueryableArray.this, typeReference, nativeType);
+
+            mClass = (Class<T>)typeReference.getRawType();
+
+            TypeReference<?> componentToken = typeReference.getComponentType();
+            mComponentMarshaler = MarshalRegistry.getMarshaler(componentToken, mNativeType);
+            mComponentClass = componentToken.getRawType();
+        }
+
+        @Override
+        public void marshal(T value, ByteBuffer buffer) {
+            int length = Array.getLength(value);
+            for (int i = 0; i < length; ++i) {
+                marshalArrayElement(mComponentMarshaler, buffer, value, i);
+            }
+        }
+
+        @Override
+        public T unmarshal(ByteBuffer buffer) {
+            Object array;
+
+            int elementSize = mComponentMarshaler.getNativeSize();
+
+            if (elementSize != Marshaler.NATIVE_SIZE_DYNAMIC) {
+                int remaining = buffer.remaining();
+                int arraySize = remaining / elementSize;
+
+                if (remaining % elementSize != 0) {
+                    throw new UnsupportedOperationException("Arrays for " + mTypeReference
+                            + " must be packed tighly into a multiple of " + elementSize
+                            + "; but there are " + (remaining % elementSize) + " left over bytes");
+                }
+
+                if (VERBOSE) {
+                    Log.v(TAG, String.format(
+                            "Attempting to unpack array (count = %d, element size = %d, bytes "
+                            + "remaining = %d) for type %s",
+                            arraySize, elementSize, remaining, mClass));
+                }
+
+                array = Array.newInstance(mComponentClass, arraySize);
+                for (int i = 0; i < arraySize; ++i) {
+                    Object elem = mComponentMarshaler.unmarshal(buffer);
+                    Array.set(array, i, elem);
+                }
+            } else {
+                // Dynamic size, use an array list.
+                ArrayList<Object> arrayList = new ArrayList<Object>();
+
+                // Assumes array is packed tightly; no unused bytes allowed
+                while (buffer.hasRemaining()) {
+                    Object elem = mComponentMarshaler.unmarshal(buffer);
+                    arrayList.add(elem);
+                }
+
+                int arraySize = arrayList.size();
+                array = copyListToArray(arrayList, Array.newInstance(mComponentClass, arraySize));
+            }
+
+            if (buffer.remaining() != 0) {
+                Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking "
+                        + mClass);
+            }
+
+            return mClass.cast(array);
+        }
+
+        @Override
+        public int getNativeSize() {
+            return NATIVE_SIZE_DYNAMIC;
+        }
+
+        @Override
+        public int calculateMarshalSize(T value) {
+            int elementSize = mComponentMarshaler.getNativeSize();
+            int arrayLength = Array.getLength(value);
+
+            if (elementSize != Marshaler.NATIVE_SIZE_DYNAMIC) {
+                // The fast way. Every element size is uniform.
+                return elementSize * arrayLength;
+            } else {
+                // The slow way. Accumulate size for each element.
+                int size = 0;
+                for (int i = 0; i < arrayLength; ++i) {
+                    size += calculateElementMarshalSize(mComponentMarshaler, value, i);
+                }
+
+                return size;
+            }
+        }
+
+        /*
+         * Helpers to avoid compiler errors regarding types with wildcards (?)
+         */
+
+        @SuppressWarnings("unchecked")
+        private <TElem> void marshalArrayElement(Marshaler<TElem> marshaler,
+                ByteBuffer buffer, Object array, int index) {
+            marshaler.marshal((TElem)Array.get(array, index), buffer);
+        }
+
+        @SuppressWarnings("unchecked")
+        private Object copyListToArray(ArrayList<?> arrayList, Object arrayDest) {
+            return arrayList.toArray((T[]) arrayDest);
+        }
+
+        @SuppressWarnings("unchecked")
+        private <TElem> int calculateElementMarshalSize(Marshaler<TElem> marshaler,
+                Object array, int index) {
+            Object elem = Array.get(array, index);
+
+            return marshaler.calculateMarshalSize((TElem) elem);
+        }
+    }
+
+    @Override
+    public Marshaler<T> createMarshaler(TypeReference<T> managedType, int nativeType) {
+        return new MarshalerArray(managedType, nativeType);
+    }
+
+    @Override
+    public boolean isTypeMappingSupported(TypeReference<T> managedType, int nativeType) {
+        // support both ConcreteType[] and GenericType<ConcreteType>[]
+        return managedType.getRawType().isArray();
+
+        // TODO: Should this recurse deeper and check that there is
+        // a valid marshaler for the ConcreteType as well?
+    }
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableBoolean.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableBoolean.java
new file mode 100644
index 0000000..4aa4b4a
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableBoolean.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal.impl;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Marshal booleans: TYPE_BYTE <-> boolean/Boolean
+ */
+public class MarshalQueryableBoolean implements MarshalQueryable<Boolean> {
+
+    private class MarshalerBoolean extends Marshaler<Boolean> {
+        protected MarshalerBoolean(TypeReference<Boolean> typeReference, int nativeType) {
+            super(MarshalQueryableBoolean.this, typeReference, nativeType);
+        }
+
+        @Override
+        public void marshal(Boolean value, ByteBuffer buffer) {
+            boolean unboxValue = value;
+            buffer.put((byte)(unboxValue ? 1 : 0));
+        }
+
+        @Override
+        public Boolean unmarshal(ByteBuffer buffer) {
+            return buffer.get() != 0;
+        }
+
+        @Override
+        public int getNativeSize() {
+            return SIZEOF_BYTE;
+        }
+    }
+
+    @Override
+    public Marshaler<Boolean> createMarshaler(TypeReference<Boolean> managedType,
+            int nativeType) {
+        return new MarshalerBoolean(managedType, nativeType);
+    }
+
+    @Override
+    public boolean isTypeMappingSupported(TypeReference<Boolean> managedType, int nativeType) {
+        return (Boolean.class.equals(managedType.getType())
+                || boolean.class.equals(managedType.getType())) && nativeType == TYPE_BYTE;
+    }
+
+
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableColorSpaceTransform.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableColorSpaceTransform.java
new file mode 100644
index 0000000..d3796db
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableColorSpaceTransform.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal.impl;
+
+import android.hardware.camera2.ColorSpaceTransform;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import java.nio.ByteBuffer;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+/**
+ * Marshal {@link ColorSpaceTransform} to/from {@link #TYPE_RATIONAL}
+ */
+public class MarshalQueryableColorSpaceTransform implements
+        MarshalQueryable<ColorSpaceTransform> {
+
+    private static final int ELEMENTS_INT32 = 3 * 3 * (SIZEOF_RATIONAL / SIZEOF_INT32);
+    private static final int SIZE = SIZEOF_INT32 * ELEMENTS_INT32;
+
+    /** rational x 3 x 3 */
+    private class MarshalerColorSpaceTransform extends Marshaler<ColorSpaceTransform> {
+        protected MarshalerColorSpaceTransform(TypeReference<ColorSpaceTransform> typeReference,
+                int nativeType) {
+            super(MarshalQueryableColorSpaceTransform.this, typeReference, nativeType);
+        }
+
+        @Override
+        public void marshal(ColorSpaceTransform value, ByteBuffer buffer) {
+            int[] transformAsArray = new int[ELEMENTS_INT32];
+            value.copyElements(transformAsArray, /*offset*/0);
+
+            for (int i = 0; i < ELEMENTS_INT32; ++i) {
+                buffer.putInt(transformAsArray[i]);
+            }
+        }
+
+        @Override
+        public ColorSpaceTransform unmarshal(ByteBuffer buffer) {
+            int[] transformAsArray = new int[ELEMENTS_INT32];
+
+            for (int i = 0; i < ELEMENTS_INT32; ++i) {
+                transformAsArray[i] = buffer.getInt();
+            }
+
+            return new ColorSpaceTransform(transformAsArray);
+        }
+
+        @Override
+        public int getNativeSize() {
+            return SIZE;
+        }
+    }
+
+    @Override
+    public Marshaler<ColorSpaceTransform> createMarshaler(
+            TypeReference<ColorSpaceTransform> managedType, int nativeType) {
+        return new MarshalerColorSpaceTransform(managedType, nativeType);
+    }
+
+    @Override
+    public boolean isTypeMappingSupported(
+            TypeReference<ColorSpaceTransform> managedType, int nativeType) {
+        return nativeType == TYPE_RATIONAL &&
+                ColorSpaceTransform.class.equals(managedType.getType());
+    }
+
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableEnum.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableEnum.java
new file mode 100644
index 0000000..fa53db2
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableEnum.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal.impl;
+
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+/**
+ * Marshal any simple enum (0-arg constructors only) into/from either
+ * {@code TYPE_BYTE} or {@code TYPE_INT32}.
+ *
+ * <p>Default values of the enum are mapped to its ordinal; this can be overridden
+ * by providing a manual value with {@link #registerEnumValues}.</p>
+
+ * @param <T> the type of {@code Enum}
+ */
+public class MarshalQueryableEnum<T extends Enum<T>> implements MarshalQueryable<T> {
+
+    private static final String TAG = MarshalQueryableEnum.class.getSimpleName();
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private static final int UINT8_MIN = 0x0;
+    private static final int UINT8_MAX = (1 << Byte.SIZE) - 1;
+    private static final int UINT8_MASK = UINT8_MAX;
+
+    private class MarshalerEnum extends Marshaler<T> {
+
+        private final Class<T> mClass;
+
+        @SuppressWarnings("unchecked")
+        protected MarshalerEnum(TypeReference<T> typeReference, int nativeType) {
+            super(MarshalQueryableEnum.this, typeReference, nativeType);
+
+            mClass = (Class<T>)typeReference.getRawType();
+        }
+
+        @Override
+        public void marshal(T value, ByteBuffer buffer) {
+            int enumValue = getEnumValue(value);
+
+            if (mNativeType == TYPE_INT32) {
+                buffer.putInt(enumValue);
+            } else if (mNativeType == TYPE_BYTE) {
+                if (enumValue < UINT8_MIN || enumValue > UINT8_MAX) {
+                    throw new UnsupportedOperationException(String.format(
+                            "Enum value %x too large to fit into unsigned byte", enumValue));
+                }
+                buffer.put((byte)enumValue);
+            } else {
+                throw new AssertionError();
+            }
+        }
+
+        @Override
+        public T unmarshal(ByteBuffer buffer) {
+            int enumValue;
+
+            switch (mNativeType) {
+                case TYPE_INT32:
+                    enumValue = buffer.getInt();
+                    break;
+                case TYPE_BYTE:
+                    // get the unsigned byte value; avoid sign extension
+                    enumValue = buffer.get() & UINT8_MASK;
+                    break;
+                default:
+                    throw new AssertionError(
+                            "Unexpected native type; impossible since its not supported");
+            }
+
+            return getEnumFromValue(mClass, enumValue);
+        }
+
+        @Override
+        public int getNativeSize() {
+            return getPrimitiveTypeSize(mNativeType);
+        }
+    }
+
+    @Override
+    public Marshaler<T> createMarshaler(TypeReference<T> managedType, int nativeType) {
+        return new MarshalerEnum(managedType, nativeType);
+    }
+
+    @Override
+    public boolean isTypeMappingSupported(TypeReference<T> managedType, int nativeType) {
+        if (nativeType == TYPE_INT32 || nativeType == TYPE_BYTE) {
+            if (managedType.getType() instanceof Class<?>) {
+                Class<?> typeClass = (Class<?>)managedType.getType();
+
+                if (typeClass.isEnum()) {
+                    if (VERBOSE) {
+                        Log.v(TAG, "possible enum detected for " + typeClass);
+                    }
+
+                    // The enum must not take extra arguments
+                    try {
+                        // match a class like: "public enum Fruits { Apple, Orange; }"
+                        typeClass.getDeclaredConstructor(String.class, int.class);
+                        return true;
+                    } catch (NoSuchMethodException e) {
+                        // Skip: custom enum with a special constructor e.g. Foo(T), but need Foo()
+                        Log.e(TAG, "Can't marshal class " + typeClass + "; no default constructor");
+                    } catch (SecurityException e) {
+                        // Skip: wouldn't be able to touch the enum anyway
+                        Log.e(TAG, "Can't marshal class " + typeClass + "; not accessible");
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    @SuppressWarnings("rawtypes")
+    private static final HashMap<Class<? extends Enum>, int[]> sEnumValues =
+            new HashMap<Class<? extends Enum>, int[]>();
+
+    /**
+     * Register a non-sequential set of values to be used with the marshal/unmarshal functions.
+     *
+     * <p>This enables get/set to correctly marshal the enum into a value that is C-compatible.</p>
+     *
+     * @param enumType The class for an enum
+     * @param values A list of values mapping to the ordinals of the enum
+     */
+    public static <T extends Enum<T>> void registerEnumValues(Class<T> enumType, int[] values) {
+        if (enumType.getEnumConstants().length != values.length) {
+            throw new IllegalArgumentException(
+                    "Expected values array to be the same size as the enumTypes values "
+                            + values.length + " for type " + enumType);
+        }
+        if (VERBOSE) {
+            Log.v(TAG, "Registered enum values for type " + enumType + " values");
+        }
+
+        sEnumValues.put(enumType, values);
+    }
+
+    /**
+     * Get the numeric value from an enum.
+     *
+     * <p>This is usually the same as the ordinal value for
+     * enums that have fully sequential values, although for C-style enums the range of values
+     * may not map 1:1.</p>
+     *
+     * @param enumValue Enum instance
+     * @return Int guaranteed to be ABI-compatible with the C enum equivalent
+     */
+    private static <T extends Enum<T>> int getEnumValue(T enumValue) {
+        int[] values;
+        values = sEnumValues.get(enumValue.getClass());
+
+        int ordinal = enumValue.ordinal();
+        if (values != null) {
+            return values[ordinal];
+        }
+
+        return ordinal;
+    }
+
+    /**
+     * Finds the enum corresponding to it's numeric value. Opposite of {@link #getEnumValue} method.
+     *
+     * @param enumType Class of the enum we want to find
+     * @param value The numeric value of the enum
+     * @return An instance of the enum
+     */
+    private static <T extends Enum<T>> T getEnumFromValue(Class<T> enumType, int value) {
+        int ordinal;
+
+        int[] registeredValues = sEnumValues.get(enumType);
+        if (registeredValues != null) {
+            ordinal = -1;
+
+            for (int i = 0; i < registeredValues.length; ++i) {
+                if (registeredValues[i] == value) {
+                    ordinal = i;
+                    break;
+                }
+            }
+        } else {
+            ordinal = value;
+        }
+
+        T[] values = enumType.getEnumConstants();
+
+        if (ordinal < 0 || ordinal >= values.length) {
+            throw new IllegalArgumentException(
+                    String.format(
+                            "Argument 'value' (%d) was not a valid enum value for type %s "
+                                    + "(registered? %b)",
+                            value,
+                            enumType, (registeredValues != null)));
+        }
+
+        return values[ordinal];
+    }
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableMeteringRectangle.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableMeteringRectangle.java
new file mode 100644
index 0000000..c8b9bd8
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableMeteringRectangle.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal.impl;
+
+import android.hardware.camera2.MeteringRectangle;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import java.nio.ByteBuffer;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+/**
+ * Marshal {@link MeteringRectangle} to/from {@link #TYPE_INT32}
+ */
+public class MarshalQueryableMeteringRectangle implements MarshalQueryable<MeteringRectangle> {
+    private static final int SIZE = SIZEOF_INT32 * 5;
+
+    /** (xmin, ymin, xmax, ymax, weight) */
+    private class MarshalerMeteringRectangle extends Marshaler<MeteringRectangle> {
+        protected MarshalerMeteringRectangle(TypeReference<MeteringRectangle> typeReference,
+                int nativeType) {
+            super(MarshalQueryableMeteringRectangle.this, typeReference, nativeType);
+        }
+
+        @Override
+        public void marshal(MeteringRectangle value, ByteBuffer buffer) {
+            int xMin = value.getX();
+            int yMin = value.getY();
+            int xMax = xMin + value.getWidth();
+            int yMax = yMin + value.getHeight();
+            int weight = value.getMeteringWeight();
+
+            buffer.putInt(xMin);
+            buffer.putInt(yMin);
+            buffer.putInt(xMax);
+            buffer.putInt(yMax);
+            buffer.putInt(weight);
+        }
+
+        @Override
+        public MeteringRectangle unmarshal(ByteBuffer buffer) {
+            int xMin = buffer.getInt();
+            int yMin = buffer.getInt();
+            int xMax = buffer.getInt();
+            int yMax = buffer.getInt();
+            int weight = buffer.getInt();
+
+            int width = xMax - xMin;
+            int height = yMax - yMin;
+
+            return new MeteringRectangle(xMin, yMin, width, height, weight);
+        }
+
+        @Override
+        public int getNativeSize() {
+            return SIZE;
+        }
+    }
+
+    @Override
+    public Marshaler<MeteringRectangle> createMarshaler(
+            TypeReference<MeteringRectangle> managedType, int nativeType) {
+        return new MarshalerMeteringRectangle(managedType, nativeType);
+    }
+
+    @Override
+    public boolean isTypeMappingSupported(
+            TypeReference<MeteringRectangle> managedType, int nativeType) {
+        return nativeType == TYPE_INT32 && MeteringRectangle.class.equals(managedType.getType());
+    }
+
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableNativeByteToInteger.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableNativeByteToInteger.java
new file mode 100644
index 0000000..3b89c82
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableNativeByteToInteger.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal.impl;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Marshal fake native enums (ints): TYPE_BYTE <-> int/Integer
+ */
+public class MarshalQueryableNativeByteToInteger implements MarshalQueryable<Integer> {
+
+    private static final int UINT8_MASK = (1 << Byte.SIZE) - 1;
+
+    private class MarshalerNativeByteToInteger extends Marshaler<Integer> {
+        protected MarshalerNativeByteToInteger(TypeReference<Integer> typeReference,
+                int nativeType) {
+            super(MarshalQueryableNativeByteToInteger.this, typeReference, nativeType);
+        }
+
+        @Override
+        public void marshal(Integer value, ByteBuffer buffer) {
+            buffer.put((byte)(int)value); // truncate down to byte
+        }
+
+        @Override
+        public Integer unmarshal(ByteBuffer buffer) {
+            // expand unsigned byte to int; avoid sign extension
+            return buffer.get() & UINT8_MASK;
+        }
+
+        @Override
+        public int getNativeSize() {
+            return SIZEOF_BYTE;
+        }
+    }
+
+    @Override
+    public Marshaler<Integer> createMarshaler(TypeReference<Integer> managedType,
+            int nativeType) {
+        return new MarshalerNativeByteToInteger(managedType, nativeType);
+    }
+
+    @Override
+    public boolean isTypeMappingSupported(TypeReference<Integer> managedType, int nativeType) {
+        return (Integer.class.equals(managedType.getType())
+                || int.class.equals(managedType.getType())) && nativeType == TYPE_BYTE;
+    }
+
+
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java
new file mode 100644
index 0000000..1fd6a1d
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal.impl;
+
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.lang.reflect.Field;
+import java.nio.ByteBuffer;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+/**
+ * Marshal any {@code T extends Parcelable} to/from any native type
+ *
+ * <p>Use with extreme caution! File descriptors and binders will not be marshaled across.</p>
+ */
+public class MarshalQueryableParcelable<T extends Parcelable>
+        implements MarshalQueryable<T> {
+
+    private static final String TAG = "MarshalParcelable";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private static final String FIELD_CREATOR = "CREATOR";
+
+    private class MarshalerParcelable extends Marshaler<T> {
+
+        private final Class<T> mClass;
+        private final Parcelable.Creator<T> mCreator;
+
+        @SuppressWarnings("unchecked")
+        protected MarshalerParcelable(TypeReference<T> typeReference,
+                int nativeType) {
+            super(MarshalQueryableParcelable.this, typeReference, nativeType);
+
+            mClass = (Class<T>)typeReference.getRawType();
+            Field creatorField;
+            try {
+                creatorField = mClass.getDeclaredField(FIELD_CREATOR);
+            } catch (NoSuchFieldException e) {
+                // Impossible. All Parcelable implementations must have a 'CREATOR' static field
+                throw new AssertionError(e);
+            }
+
+            try {
+                mCreator = (Parcelable.Creator<T>)creatorField.get(null);
+            } catch (IllegalAccessException e) {
+                // Impossible: All 'CREATOR' static fields must be public
+                throw new AssertionError(e);
+            } catch (IllegalArgumentException e) {
+                // Impossible: This is a static field, so null must be ok
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public void marshal(T value, ByteBuffer buffer) {
+            if (VERBOSE) {
+                Log.v(TAG, "marshal " + value);
+            }
+
+            Parcel parcel = Parcel.obtain();
+            byte[] parcelContents;
+
+            try {
+                value.writeToParcel(parcel, /*flags*/0);
+
+                if (parcel.hasFileDescriptors()) {
+                    throw new UnsupportedOperationException(
+                            "Parcelable " + value + " must not have file descriptors");
+                }
+
+                parcelContents = parcel.marshall();
+            }
+            finally {
+                parcel.recycle();
+            }
+
+            if (parcelContents.length == 0) {
+                throw new AssertionError("No data marshaled for " + value);
+            }
+
+            buffer.put(parcelContents);
+        }
+
+        @Override
+        public T unmarshal(ByteBuffer buffer) {
+            if (VERBOSE) {
+                Log.v(TAG, "unmarshal, buffer remaining " + buffer.remaining());
+            }
+
+            /*
+             * Quadratically slow when marshaling an array of parcelables.
+             *
+             * Read out the entire byte buffer as an array, then copy it into the parcel.
+             *
+             * Once we unparcel the entire object, advance the byte buffer by only how many
+             * bytes the parcel actually used up.
+             *
+             * Future: If we ever do need to use parcelable arrays, we can do this a little smarter
+             * by reading out a chunk like 4,8,16,24 each time, but not sure how to detect
+             * parcels being too short in this case.
+             *
+             * Future: Alternatively use Parcel#obtain(long) directly into the native
+             * pointer of a ByteBuffer, which would not copy if the ByteBuffer was direct.
+             */
+            buffer.mark();
+
+            Parcel parcel = Parcel.obtain();
+            try {
+                int maxLength = buffer.remaining();
+
+                byte[] remaining = new byte[maxLength];
+                buffer.get(remaining);
+
+                parcel.unmarshall(remaining, /*offset*/0, maxLength);
+                parcel.setDataPosition(/*pos*/0);
+
+                T value = mCreator.createFromParcel(parcel);
+                int actualLength = parcel.dataPosition();
+
+                if (actualLength == 0) {
+                    throw new AssertionError("No data marshaled for " + value);
+                }
+
+                // set the position past the bytes the parcelable actually used
+                buffer.reset();
+                buffer.position(buffer.position() + actualLength);
+
+                if (VERBOSE) {
+                    Log.v(TAG, "unmarshal, parcel length was " + actualLength);
+                    Log.v(TAG, "unmarshal, value is " + value);
+                }
+
+                return mClass.cast(value);
+            } finally {
+                parcel.recycle();
+            }
+        }
+
+        @Override
+        public int getNativeSize() {
+            return NATIVE_SIZE_DYNAMIC;
+        }
+
+        @Override
+        public int calculateMarshalSize(T value) {
+            Parcel parcel = Parcel.obtain();
+            try {
+                value.writeToParcel(parcel, /*flags*/0);
+                int length = parcel.marshall().length;
+
+                if (VERBOSE) {
+                    Log.v(TAG, "calculateMarshalSize, length when parceling "
+                            + value + " is " + length);
+                }
+
+                return length;
+            } finally {
+                parcel.recycle();
+            }
+        }
+    }
+
+    @Override
+    public Marshaler<T> createMarshaler(TypeReference<T> managedType, int nativeType) {
+        return new MarshalerParcelable(managedType, nativeType);
+    }
+
+    @Override
+    public boolean isTypeMappingSupported(TypeReference<T> managedType, int nativeType) {
+        return Parcelable.class.isAssignableFrom(managedType.getRawType());
+    }
+
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryablePrimitive.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryablePrimitive.java
new file mode 100644
index 0000000..708da70
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryablePrimitive.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal.impl;
+
+import android.hardware.camera2.Rational;
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+import static com.android.internal.util.Preconditions.*;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Marshal/unmarshal built-in primitive types to and from a {@link ByteBuffer}.
+ *
+ * <p>The following list of type marshaling is supported:
+ * <ul>
+ * <li>byte <-> TYPE_BYTE
+ * <li>int <-> TYPE_INT32
+ * <li>long <-> TYPE_INT64
+ * <li>float <-> TYPE_FLOAT
+ * <li>double <-> TYPE_DOUBLE
+ * <li>Rational <-> TYPE_RATIONAL
+ * </ul>
+ * </p>
+ *
+ * <p>Due to the nature of generics, values are always boxed; this also means that both
+ * the boxed and unboxed types are supported (i.e. both {@code int} and {@code Integer}).</p>
+ *
+ * <p>Each managed type <!--(other than boolean)--> must correspond 1:1 to the native type
+ * (e.g. a byte will not map to a {@link CameraMetadataNative#TYPE_INT32 TYPE_INT32} or vice versa)
+ * for marshaling.</p>
+ */
+public final class MarshalQueryablePrimitive<T> implements MarshalQueryable<T> {
+
+    private class MarshalerPrimitive extends Marshaler<T> {
+        /** Always the wrapped class variant of the primitive class for {@code T} */
+        private final Class<T> mClass;
+
+        @SuppressWarnings("unchecked")
+        protected MarshalerPrimitive(TypeReference<T> typeReference, int nativeType) {
+            super(MarshalQueryablePrimitive.this, typeReference, nativeType);
+
+            // Turn primitives into wrappers, otherwise int.class.cast(Integer) will fail
+            mClass = wrapClassIfPrimitive((Class<T>)typeReference.getRawType());
+        }
+
+        @Override
+        public T unmarshal(ByteBuffer buffer) {
+            return mClass.cast(unmarshalObject(buffer));
+        }
+
+        @Override
+        public int calculateMarshalSize(T value) {
+            return getPrimitiveTypeSize(mNativeType);
+        }
+
+        @Override
+        public void marshal(T value, ByteBuffer buffer) {
+            if (value instanceof Integer) {
+                checkNativeTypeEquals(TYPE_INT32, mNativeType);
+                final int val = (Integer) value;
+                marshalPrimitive(val, buffer);
+            } else if (value instanceof Float) {
+                checkNativeTypeEquals(TYPE_FLOAT, mNativeType);
+                final float val = (Float) value;
+                marshalPrimitive(val, buffer);
+            } else if (value instanceof Long) {
+                checkNativeTypeEquals(TYPE_INT64, mNativeType);
+                final long val = (Long) value;
+                marshalPrimitive(val, buffer);
+            } else if (value instanceof Rational) {
+                checkNativeTypeEquals(TYPE_RATIONAL, mNativeType);
+                marshalPrimitive((Rational) value, buffer);
+            } else if (value instanceof Double) {
+                checkNativeTypeEquals(TYPE_DOUBLE, mNativeType);
+                final double val = (Double) value;
+                marshalPrimitive(val, buffer);
+            } else if (value instanceof Byte) {
+                checkNativeTypeEquals(TYPE_BYTE, mNativeType);
+                final byte val = (Byte) value;
+                marshalPrimitive(val, buffer);
+            } else {
+                throw new UnsupportedOperationException(
+                        "Can't marshal managed type " + mTypeReference);
+            }
+        }
+
+        private void marshalPrimitive(int value, ByteBuffer buffer) {
+            buffer.putInt(value);
+        }
+
+        private void marshalPrimitive(float value, ByteBuffer buffer) {
+            buffer.putFloat(value);
+        }
+
+        private void marshalPrimitive(double value, ByteBuffer buffer) {
+            buffer.putDouble(value);
+        }
+
+        private void marshalPrimitive(long value, ByteBuffer buffer) {
+            buffer.putLong(value);
+        }
+
+        private void marshalPrimitive(Rational value, ByteBuffer buffer) {
+            buffer.putInt(value.getNumerator());
+            buffer.putInt(value.getDenominator());
+        }
+
+        private void marshalPrimitive(byte value, ByteBuffer buffer) {
+            buffer.put(value);
+        }
+
+        private Object unmarshalObject(ByteBuffer buffer) {
+            switch (mNativeType) {
+                case TYPE_INT32:
+                    return buffer.getInt();
+                case TYPE_FLOAT:
+                    return buffer.getFloat();
+                case TYPE_INT64:
+                    return buffer.getLong();
+                case TYPE_RATIONAL:
+                    int numerator = buffer.getInt();
+                    int denominator = buffer.getInt();
+                    return new Rational(numerator, denominator);
+                case TYPE_DOUBLE:
+                    return buffer.getDouble();
+                case TYPE_BYTE:
+                    return buffer.get(); // getByte
+                default:
+                    throw new UnsupportedOperationException(
+                            "Can't unmarshal native type " + mNativeType);
+            }
+        }
+
+        @Override
+        public int getNativeSize() {
+            return getPrimitiveTypeSize(mNativeType);
+        }
+    }
+
+    @Override
+    public Marshaler<T> createMarshaler(TypeReference<T> managedType, int nativeType) {
+        return new MarshalerPrimitive(managedType, nativeType);
+    }
+
+    @Override
+    public boolean isTypeMappingSupported(TypeReference<T> managedType, int nativeType) {
+        if (managedType.getType() instanceof Class<?>) {
+            Class<?> klass = (Class<?>)managedType.getType();
+
+            if (klass == byte.class || klass == Byte.class) {
+                return nativeType == TYPE_BYTE;
+            } else if (klass == int.class || klass == Integer.class) {
+                return nativeType == TYPE_INT32;
+            } else if (klass == float.class || klass == Float.class) {
+                return nativeType == TYPE_FLOAT;
+            } else if (klass == long.class || klass == Long.class) {
+                return nativeType == TYPE_INT64;
+            } else if (klass == double.class || klass == Double.class) {
+                return nativeType == TYPE_DOUBLE;
+            } else if (klass == Rational.class) {
+                return nativeType == TYPE_RATIONAL;
+            }
+        }
+        return false;
+    }
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRange.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRange.java
new file mode 100644
index 0000000..8512804
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRange.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal.impl;
+
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.marshal.MarshalRegistry;
+import android.hardware.camera2.utils.TypeReference;
+import android.util.Range;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.nio.ByteBuffer;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+/**
+ * Marshal {@link Range} to/from any native type
+ */
+public class MarshalQueryableRange<T extends Comparable<? super T>>
+        implements MarshalQueryable<Range<T>> {
+    private static final int RANGE_COUNT = 2;
+
+    private class MarshalerRange extends Marshaler<Range<T>> {
+        private final Class<? super Range<T>> mClass;
+        private final Constructor<Range<T>> mConstructor;
+        /** Marshal the {@code T} inside of {@code Range<T>} */
+        private final Marshaler<T> mNestedTypeMarshaler;
+
+        @SuppressWarnings("unchecked")
+        protected MarshalerRange(TypeReference<Range<T>> typeReference,
+                int nativeType) {
+            super(MarshalQueryableRange.this, typeReference, nativeType);
+
+            mClass = typeReference.getRawType();
+
+            /*
+             * Lookup the actual type argument, e.g. Range<Integer> --> Integer
+             * and then get the marshaler for that managed type.
+             */
+            ParameterizedType paramType;
+            try {
+                paramType = (ParameterizedType) typeReference.getType();
+            } catch (ClassCastException e) {
+                throw new AssertionError("Raw use of Range is not supported", e);
+            }
+            Type actualTypeArgument = paramType.getActualTypeArguments()[0];
+
+            TypeReference<?> actualTypeArgToken =
+                    TypeReference.createSpecializedTypeReference(actualTypeArgument);
+
+            mNestedTypeMarshaler = (Marshaler<T>)MarshalRegistry.getMarshaler(
+                    actualTypeArgToken, mNativeType);
+            try {
+                mConstructor = (Constructor<Range<T>>)mClass.getConstructor(
+                        Comparable.class, Comparable.class);
+            } catch (NoSuchMethodException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public void marshal(Range<T> value, ByteBuffer buffer) {
+            mNestedTypeMarshaler.marshal(value.getLower(), buffer);
+            mNestedTypeMarshaler.marshal(value.getUpper(), buffer);
+        }
+
+        @Override
+        public Range<T> unmarshal(ByteBuffer buffer) {
+            T lower = mNestedTypeMarshaler.unmarshal(buffer);
+            T upper = mNestedTypeMarshaler.unmarshal(buffer);
+
+            try {
+                return mConstructor.newInstance(lower, upper);
+            } catch (InstantiationException e) {
+                throw new AssertionError(e);
+            } catch (IllegalAccessException e) {
+                throw new AssertionError(e);
+            } catch (IllegalArgumentException e) {
+                throw new AssertionError(e);
+            } catch (InvocationTargetException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public int getNativeSize() {
+            int nestedSize = mNestedTypeMarshaler.getNativeSize();
+
+            if (nestedSize != NATIVE_SIZE_DYNAMIC) {
+                return nestedSize * RANGE_COUNT;
+            } else {
+                return NATIVE_SIZE_DYNAMIC;
+            }
+        }
+
+        @Override
+        public int calculateMarshalSize(Range<T> value) {
+            int nativeSize = getNativeSize();
+
+            if (nativeSize != NATIVE_SIZE_DYNAMIC) {
+                return nativeSize;
+            } else {
+                int lowerSize = mNestedTypeMarshaler.calculateMarshalSize(value.getLower());
+                int upperSize = mNestedTypeMarshaler.calculateMarshalSize(value.getUpper());
+
+                return lowerSize + upperSize;
+            }
+        }
+    }
+
+    @Override
+    public Marshaler<Range<T>> createMarshaler(TypeReference<Range<T>> managedType,
+            int nativeType) {
+        return new MarshalerRange(managedType, nativeType);
+    }
+
+    @Override
+    public boolean isTypeMappingSupported(TypeReference<Range<T>> managedType, int nativeType) {
+        return (Range.class.equals(managedType.getRawType()));
+    }
+
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRect.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRect.java
new file mode 100644
index 0000000..de20a1f
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRect.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal.impl;
+
+import android.graphics.Rect;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import java.nio.ByteBuffer;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+/**
+ * Marshal {@link Rect} to/from {@link #TYPE_INT32}
+ */
+public class MarshalQueryableRect implements MarshalQueryable<Rect> {
+    private static final int SIZE = SIZEOF_INT32 * 4;
+
+    private class MarshalerRect extends Marshaler<Rect> {
+        protected MarshalerRect(TypeReference<Rect> typeReference,
+                int nativeType) {
+            super(MarshalQueryableRect.this, typeReference, nativeType);
+        }
+
+        @Override
+        public void marshal(Rect value, ByteBuffer buffer) {
+            buffer.putInt(value.left);
+            buffer.putInt(value.top);
+            buffer.putInt(value.width());
+            buffer.putInt(value.height());
+        }
+
+        @Override
+        public Rect unmarshal(ByteBuffer buffer) {
+            int left = buffer.getInt();
+            int top = buffer.getInt();
+            int width = buffer.getInt();
+            int height = buffer.getInt();
+
+            int right = left + width;
+            int bottom = top + height;
+
+            return new Rect(left, top, right, bottom);
+        }
+
+        @Override
+        public int getNativeSize() {
+            return SIZE;
+        }
+    }
+
+    @Override
+    public Marshaler<Rect> createMarshaler(TypeReference<Rect> managedType, int nativeType) {
+        return new MarshalerRect(managedType, nativeType);
+    }
+
+    @Override
+    public boolean isTypeMappingSupported(TypeReference<Rect> managedType, int nativeType) {
+        return nativeType == TYPE_INT32 && (Rect.class.equals(managedType.getType()));
+    }
+
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableReprocessFormatsMap.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableReprocessFormatsMap.java
new file mode 100644
index 0000000..3025cb4
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableReprocessFormatsMap.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal.impl;
+
+import android.hardware.camera2.ReprocessFormatsMap;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+
+/**
+ * Marshaler for {@code android.scaler.availableInputOutputFormatsMap} custom class
+ * {@link ReprocessFormatsMap}
+ */
+public class MarshalQueryableReprocessFormatsMap
+        implements MarshalQueryable<ReprocessFormatsMap> {
+
+    private class MarshalerReprocessFormatsMap extends Marshaler<ReprocessFormatsMap> {
+        protected MarshalerReprocessFormatsMap(
+                TypeReference<ReprocessFormatsMap> typeReference, int nativeType) {
+            super(MarshalQueryableReprocessFormatsMap.this, typeReference, nativeType);
+        }
+
+        @Override
+        public void marshal(ReprocessFormatsMap value, ByteBuffer buffer) {
+            /*
+             * // writing (static example, DNG+ZSL)
+             * int32_t[] contents = {
+             *   RAW_OPAQUE, 3, RAW16, YUV_420_888, BLOB,
+             *   RAW16, 2, YUV_420_888, BLOB,
+             *   ...,
+             *   INPUT_FORMAT, OUTPUT_FORMAT_COUNT, [OUTPUT_0, OUTPUT_1, ..., OUTPUT_FORMAT_COUNT-1]
+             * };
+             */
+            int[] inputs = value.getInputs();
+            for (int input : inputs) {
+                // INPUT_FORMAT
+                buffer.putInt(input);
+
+                int[] outputs = value.getOutputs(input);
+                // OUTPUT_FORMAT_COUNT
+                buffer.putInt(outputs.length);
+
+                // [OUTPUT_0, OUTPUT_1, ..., OUTPUT_FORMAT_COUNT-1]
+                for (int output : outputs) {
+                    buffer.putInt(output);
+                }
+            }
+        }
+
+        @Override
+        public ReprocessFormatsMap unmarshal(ByteBuffer buffer) {
+            int len = buffer.remaining() / SIZEOF_INT32;
+            if (buffer.remaining() % SIZEOF_INT32 != 0) {
+                throw new AssertionError("ReprocessFormatsMap was not TYPE_INT32");
+            }
+
+            int[] entries = new int[len];
+
+            IntBuffer intBuffer = buffer.asIntBuffer();
+            intBuffer.get(entries);
+
+            // TODO: consider moving rest of parsing code from ReprocessFormatsMap to here
+
+            return new ReprocessFormatsMap(entries);
+        }
+
+        @Override
+        public int getNativeSize() {
+            return NATIVE_SIZE_DYNAMIC;
+        }
+
+        @Override
+        public int calculateMarshalSize(ReprocessFormatsMap value) {
+            /*
+             * // writing (static example, DNG+ZSL)
+             * int32_t[] contents = {
+             *   RAW_OPAQUE, 3, RAW16, YUV_420_888, BLOB,
+             *   RAW16, 2, YUV_420_888, BLOB,
+             *   ...,
+             *   INPUT_FORMAT, OUTPUT_FORMAT_COUNT, [OUTPUT_0, OUTPUT_1, ..., OUTPUT_FORMAT_COUNT-1]
+             * };
+             */
+            int length = 0;
+
+            int[] inputs = value.getInputs();
+            for (int input : inputs) {
+
+                length += 1; // INPUT_FORMAT
+                length += 1; // OUTPUT_FORMAT_COUNT
+
+                int[] outputs = value.getOutputs(input);
+                length += outputs.length; // [OUTPUT_0, OUTPUT_1, ..., OUTPUT_FORMAT_COUNT-1]
+            }
+
+            return length * SIZEOF_INT32;
+        }
+    }
+
+    @Override
+    public Marshaler<ReprocessFormatsMap> createMarshaler(
+            TypeReference<ReprocessFormatsMap> managedType, int nativeType) {
+        return new MarshalerReprocessFormatsMap(managedType, nativeType);
+    }
+
+    @Override
+    public boolean isTypeMappingSupported(TypeReference<ReprocessFormatsMap> managedType,
+            int nativeType) {
+        return nativeType == TYPE_INT32 && managedType.getType().equals(ReprocessFormatsMap.class);
+    }
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRggbChannelVector.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRggbChannelVector.java
new file mode 100644
index 0000000..93c0e92
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRggbChannelVector.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal.impl;
+
+import android.hardware.camera2.RggbChannelVector;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import java.nio.ByteBuffer;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+/**
+ * Marshal {@link RggbChannelVector} to/from {@link #TYPE_FLOAT} {@code x 4}
+ */
+public class MarshalQueryableRggbChannelVector implements MarshalQueryable<RggbChannelVector> {
+    private static final int SIZE = SIZEOF_FLOAT * RggbChannelVector.COUNT;
+
+    private class MarshalerRggbChannelVector extends Marshaler<RggbChannelVector> {
+        protected MarshalerRggbChannelVector(TypeReference<RggbChannelVector> typeReference,
+                int nativeType) {
+            super(MarshalQueryableRggbChannelVector.this, typeReference, nativeType);
+        }
+
+        @Override
+        public void marshal(RggbChannelVector value, ByteBuffer buffer) {
+            for (int i = 0; i < RggbChannelVector.COUNT; ++i) {
+                buffer.putFloat(value.getComponent(i));
+            }
+        }
+
+        @Override
+        public RggbChannelVector unmarshal(ByteBuffer buffer) {
+            float red = buffer.getFloat();
+            float gEven = buffer.getFloat();
+            float gOdd = buffer.getFloat();
+            float blue = buffer.getFloat();
+
+            return new RggbChannelVector(red, gEven, gOdd, blue);
+        }
+
+        @Override
+        public int getNativeSize() {
+            return SIZE;
+        }
+    }
+
+    @Override
+    public Marshaler<RggbChannelVector> createMarshaler(
+            TypeReference<RggbChannelVector> managedType, int nativeType) {
+        return new MarshalerRggbChannelVector(managedType, nativeType);
+    }
+
+    @Override
+    public boolean isTypeMappingSupported(
+            TypeReference<RggbChannelVector> managedType, int nativeType) {
+        return nativeType == TYPE_FLOAT && (RggbChannelVector.class.equals(managedType.getType()));
+    }
+
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSize.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSize.java
new file mode 100644
index 0000000..6a73bee
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSize.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal.impl;
+
+import android.hardware.camera2.Size;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Marshal {@link Size} to/from {@code TYPE_INT32}
+ */
+public class MarshalQueryableSize implements MarshalQueryable<Size> {
+    private static final int SIZE = SIZEOF_INT32 * 2;
+
+    private class MarshalerSize extends Marshaler<Size> {
+        protected MarshalerSize(TypeReference<Size> typeReference, int nativeType) {
+            super(MarshalQueryableSize.this, typeReference, nativeType);
+        }
+
+        @Override
+        public void marshal(Size value, ByteBuffer buffer) {
+            buffer.putInt(value.getWidth());
+            buffer.putInt(value.getHeight());
+        }
+
+        @Override
+        public Size unmarshal(ByteBuffer buffer) {
+            int width = buffer.getInt();
+            int height = buffer.getInt();
+
+            return new Size(width, height);
+        }
+
+        @Override
+        public int getNativeSize() {
+            return SIZE;
+        }
+    }
+
+    @Override
+    public Marshaler<Size> createMarshaler(TypeReference<Size> managedType, int nativeType) {
+        return new MarshalerSize(managedType, nativeType);
+    }
+
+    @Override
+    public boolean isTypeMappingSupported(TypeReference<Size> managedType, int nativeType) {
+        return nativeType == TYPE_INT32 && (Size.class.equals(managedType.getType()));
+    }
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSizeF.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSizeF.java
new file mode 100644
index 0000000..b60a46d
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSizeF.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal.impl;
+
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+import android.util.SizeF;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Marshal {@link SizeF} to/from {@code TYPE_FLOAT}
+ */
+public class MarshalQueryableSizeF implements MarshalQueryable<SizeF> {
+
+    private static final int SIZE = SIZEOF_FLOAT * 2;
+
+    private class MarshalerSizeF extends Marshaler<SizeF> {
+
+        protected MarshalerSizeF(TypeReference<SizeF> typeReference, int nativeType) {
+            super(MarshalQueryableSizeF.this, typeReference, nativeType);
+        }
+
+        @Override
+        public void marshal(SizeF value, ByteBuffer buffer) {
+            buffer.putFloat(value.getWidth());
+            buffer.putFloat(value.getHeight());
+        }
+
+        @Override
+        public SizeF unmarshal(ByteBuffer buffer) {
+            float width = buffer.getFloat();
+            float height = buffer.getFloat();
+
+            return new SizeF(width, height);
+        }
+
+        @Override
+        public int getNativeSize() {
+            return SIZE;
+        }
+    }
+
+    @Override
+    public Marshaler<SizeF> createMarshaler(
+            TypeReference<SizeF> managedType, int nativeType) {
+        return new MarshalerSizeF(managedType, nativeType);
+    }
+
+    @Override
+    public boolean isTypeMappingSupported(TypeReference<SizeF> managedType, int nativeType) {
+        return nativeType == TYPE_FLOAT && (SizeF.class.equals(managedType.getType()));
+    }
+}
+
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfiguration.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfiguration.java
new file mode 100644
index 0000000..6a4e821
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfiguration.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal.impl;
+
+import android.hardware.camera2.StreamConfiguration;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Marshaler for {@code android.scaler.availableStreamConfigurations} custom class
+ * {@link StreamConfiguration}
+ *
+ * <p>Data is stored as {@code (format, width, height, input?)} tuples (int32).</p>
+ */
+public class MarshalQueryableStreamConfiguration
+        implements MarshalQueryable<StreamConfiguration> {
+    private static final int SIZE = SIZEOF_INT32 * 4;
+
+    private class MarshalerStreamConfiguration extends Marshaler<StreamConfiguration> {
+        protected MarshalerStreamConfiguration(TypeReference<StreamConfiguration> typeReference,
+                int nativeType) {
+            super(MarshalQueryableStreamConfiguration.this, typeReference, nativeType);
+        }
+
+        @Override
+        public void marshal(StreamConfiguration value, ByteBuffer buffer) {
+            buffer.putInt(value.getFormat());
+            buffer.putInt(value.getWidth());
+            buffer.putInt(value.getHeight());
+            buffer.putInt(value.isInput() ? 1 : 0);
+        }
+
+        @Override
+        public StreamConfiguration unmarshal(ByteBuffer buffer) {
+            int format = buffer.getInt();
+            int width = buffer.getInt();
+            int height = buffer.getInt();
+            boolean input = buffer.getInt() != 0;
+
+            return new StreamConfiguration(format, width, height, input);
+        }
+
+        @Override
+        public int getNativeSize() {
+            return SIZE;
+        }
+
+    }
+
+    @Override
+    public Marshaler<StreamConfiguration> createMarshaler(
+            TypeReference<StreamConfiguration> managedType, int nativeType) {
+        return new MarshalerStreamConfiguration(managedType, nativeType);
+    }
+
+    @Override
+    public boolean isTypeMappingSupported(TypeReference<StreamConfiguration> managedType,
+            int nativeType) {
+        return nativeType == TYPE_INT32 && managedType.getType().equals(StreamConfiguration.class);
+    }
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfigurationDuration.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfigurationDuration.java
new file mode 100644
index 0000000..c3d564e
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfigurationDuration.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal.impl;
+
+import android.hardware.camera2.StreamConfigurationDuration;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Marshaler for custom class {@link StreamConfigurationDuration} for min-frame and stall durations.
+ *
+ * <p>
+ * Data is stored as {@code (format, width, height, durationNs)} tuples (int64).
+ * </p>
+ */
+public class MarshalQueryableStreamConfigurationDuration
+        implements MarshalQueryable<StreamConfigurationDuration> {
+
+    private static final int SIZE = SIZEOF_INT64 * 4;
+    /**
+     * Values and-ed with this will do an unsigned int to signed long conversion;
+     * in other words the sign bit from the int will not be extended.
+     * */
+    private static final long MASK_UNSIGNED_INT = 0x00000000ffffffffL;
+
+    private class MarshalerStreamConfigurationDuration
+        extends Marshaler<StreamConfigurationDuration> {
+
+        protected MarshalerStreamConfigurationDuration(
+                TypeReference<StreamConfigurationDuration> typeReference, int nativeType) {
+            super(MarshalQueryableStreamConfigurationDuration.this, typeReference, nativeType);
+        }
+
+        @Override
+        public void marshal(StreamConfigurationDuration value, ByteBuffer buffer) {
+            buffer.putLong(value.getFormat() & MASK_UNSIGNED_INT); // unsigned int -> long
+            buffer.putLong(value.getWidth());
+            buffer.putLong(value.getHeight());
+            buffer.putLong(value.getDuration());
+        }
+
+        @Override
+        public StreamConfigurationDuration unmarshal(ByteBuffer buffer) {
+            int format = (int)buffer.getLong();
+            int width = (int)buffer.getLong();
+            int height = (int)buffer.getLong();
+            long durationNs = buffer.getLong();
+
+            return new StreamConfigurationDuration(format, width, height, durationNs);
+        }
+
+        @Override
+        public int getNativeSize() {
+            return SIZE;
+        }
+    }
+
+    @Override
+    public Marshaler<StreamConfigurationDuration> createMarshaler(
+            TypeReference<StreamConfigurationDuration> managedType, int nativeType) {
+        return new MarshalerStreamConfigurationDuration(managedType, nativeType);
+    }
+
+    @Override
+    public boolean isTypeMappingSupported(TypeReference<StreamConfigurationDuration> managedType,
+            int nativeType) {
+        return nativeType == TYPE_INT64 &&
+                (StreamConfigurationDuration.class.equals(managedType.getType()));
+    }
+
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableString.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableString.java
new file mode 100644
index 0000000..bf518bb
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableString.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.marshal.impl;
+
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+
+/**
+ * Marshal {@link String} to/from {@link #TYPE_BYTE}.
+ */
+public class MarshalQueryableString implements MarshalQueryable<String> {
+
+    private static final String TAG = MarshalQueryableString.class.getSimpleName();
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
+    private static final byte NUL = (byte)'\0'; // used as string terminator
+
+    private class MarshalerString extends Marshaler<String> {
+
+        protected MarshalerString(TypeReference<String> typeReference, int nativeType) {
+            super(MarshalQueryableString.this, typeReference, nativeType);
+        }
+
+        @Override
+        public void marshal(String value, ByteBuffer buffer) {
+            byte[] arr = value.getBytes(UTF8_CHARSET);
+
+            buffer.put(arr);
+            buffer.put(NUL); // metadata strings are NUL-terminated
+        }
+
+        @Override
+        public int calculateMarshalSize(String value) {
+            byte[] arr = value.getBytes(UTF8_CHARSET);
+
+            return arr.length + 1; // metadata strings are NUL-terminated
+        }
+
+        @Override
+        public String unmarshal(ByteBuffer buffer) {
+            buffer.mark(); // save the current position
+
+            boolean foundNull = false;
+            int stringLength = 0;
+            while (buffer.hasRemaining()) {
+                if (buffer.get() == NUL) {
+                    foundNull = true;
+                    break;
+                }
+
+                stringLength++;
+            }
+
+            if (VERBOSE) {
+                Log.v(TAG,
+                        "unmarshal - scanned " + stringLength + " characters; found null? "
+                                + foundNull);
+            }
+
+            if (!foundNull) {
+                throw new UnsupportedOperationException("Strings must be null-terminated");
+            }
+
+            buffer.reset(); // go back to the previously marked position
+
+            byte[] strBytes = new byte[stringLength + 1];
+            buffer.get(strBytes, /*dstOffset*/0, stringLength + 1); // including null character
+
+            // not including null character
+            return new String(strBytes, /*offset*/0, stringLength, UTF8_CHARSET);
+        }
+
+        @Override
+        public int getNativeSize() {
+            return NATIVE_SIZE_DYNAMIC;
+        }
+    }
+
+    @Override
+    public Marshaler<String> createMarshaler(
+            TypeReference<String> managedType, int nativeType) {
+        return new MarshalerString(managedType, nativeType);
+    }
+
+    @Override
+    public boolean isTypeMappingSupported(TypeReference<String> managedType, int nativeType) {
+        return nativeType == TYPE_BYTE && String.class.equals(managedType.getType());
+    }
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/package.html b/core/java/android/hardware/camera2/marshal/impl/package.html
new file mode 100644
index 0000000..783d0a1
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/package.html
@@ -0,0 +1,3 @@
+<body>
+{@hide}
+</body>
diff --git a/core/java/android/hardware/camera2/marshal/package.html b/core/java/android/hardware/camera2/marshal/package.html
new file mode 100644
index 0000000..783d0a1
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/package.html
@@ -0,0 +1,3 @@
+<body>
+{@hide}
+</body>
diff --git a/core/java/android/hardware/camera2/impl/HashCodeHelpers.java b/core/java/android/hardware/camera2/utils/HashCodeHelpers.java
similarity index 98%
rename from core/java/android/hardware/camera2/impl/HashCodeHelpers.java
rename to core/java/android/hardware/camera2/utils/HashCodeHelpers.java
index 2d63827..b980549 100644
--- a/core/java/android/hardware/camera2/impl/HashCodeHelpers.java
+++ b/core/java/android/hardware/camera2/utils/HashCodeHelpers.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.hardware.camera2.impl;
+package android.hardware.camera2.utils;
 
 /**
  * Provide hashing functions using the Modified Bernstein hash
diff --git a/core/java/android/hardware/camera2/utils/TypeReference.java b/core/java/android/hardware/camera2/utils/TypeReference.java
new file mode 100644
index 0000000..d0c919c
--- /dev/null
+++ b/core/java/android/hardware/camera2/utils/TypeReference.java
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.utils;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Super type token; allows capturing generic types at runtime by forcing them to be reified.
+ *
+ * <p>Usage example: <pre>{@code
+ *      // using anonymous classes (preferred)
+ *      TypeReference&lt;Integer> intToken = new TypeReference&lt;Integer>() {{ }};
+ *
+ *      // using named classes
+ *      class IntTypeReference extends TypeReference&lt;Integer> {...}
+ *      TypeReference&lt;Integer> intToken = new IntTypeReference();
+ * }</p></pre>
+ *
+ * <p>Unlike the reference implementation, this bans nested TypeVariables; that is all
+ * dynamic types must equal to the static types.</p>
+ *
+ * <p>See <a href="http://gafter.blogspot.com/2007/05/limitation-of-super-type-tokens.html">
+ * http://gafter.blogspot.com/2007/05/limitation-of-super-type-tokens.html</a>
+ * for more details.</p>
+ */
+public abstract class TypeReference<T> {
+    private final Type mType;
+
+    /**
+     * Create a new type reference for {@code T}.
+     *
+     * @throws IllegalArgumentException if {@code T}'s actual type contains a type variable
+     *
+     * @see TypeReference
+     */
+    protected TypeReference() {
+        ParameterizedType thisType = (ParameterizedType)getClass().getGenericSuperclass();
+
+        // extract the "T" from TypeReference<T>
+        mType = thisType.getActualTypeArguments()[0];
+
+        /*
+         * Prohibit type references with type variables such as
+         *
+         *    class GenericListToken<T> extends TypeReference<List<T>>
+         *
+         * Since the "T" there is not known without an instance of T, type equality would
+         * consider *all* Lists equal regardless of T. Allowing this would defeat
+         * some of the type safety of a type reference.
+         */
+        if (containsTypeVariable(mType)) {
+            throw new IllegalArgumentException(
+                    "Including a type variable in a type reference is not allowed");
+        }
+    }
+
+    /**
+     * Return the dynamic {@link Type} corresponding to the captured type {@code T}.
+     */
+    public Type getType() {
+        return mType;
+    }
+
+    private TypeReference(Type type) {
+        mType = type;
+
+        if (containsTypeVariable(mType)) {
+            throw new IllegalArgumentException(
+                    "Including a type variable in a type reference is not allowed");
+        }
+    }
+
+    private static class SpecializedTypeReference<T> extends TypeReference<T> {
+        public SpecializedTypeReference(Class<T> klass) {
+            super(klass);
+        }
+    }
+
+    @SuppressWarnings("rawtypes")
+    private static class SpecializedBaseTypeReference extends TypeReference {
+        public SpecializedBaseTypeReference(Type type) {
+            super(type);
+        }
+    }
+
+    /**
+     * Create a specialized type reference from a dynamic class instance,
+     * bypassing the standard compile-time checks.
+     *
+     * <p>As with a regular type reference, the {@code klass} must not contain
+     * any type variables.</p>
+     *
+     * @param klass a non-{@code null} {@link Class} instance
+     *
+     * @return a type reference which captures {@code T} at runtime
+     *
+     * @throws IllegalArgumentException if {@code T} had any type variables
+     */
+    public static <T> TypeReference<T> createSpecializedTypeReference(Class<T> klass) {
+        return new SpecializedTypeReference<T>(klass);
+    }
+
+    /**
+     * Create a specialized type reference from a dynamic {@link Type} instance,
+     * bypassing the standard compile-time checks.
+     *
+     * <p>As with a regular type reference, the {@code type} must not contain
+     * any type variables.</p>
+     *
+     * @param type a non-{@code null} {@link Type} instance
+     *
+     * @return a type reference which captures {@code T} at runtime
+     *
+     * @throws IllegalArgumentException if {@code type} had any type variables
+     */
+    public static TypeReference<?> createSpecializedTypeReference(Type type) {
+        return new SpecializedBaseTypeReference(type);
+    }
+
+    /**
+     * Returns the raw type of T.
+     *
+     * <p><ul>
+     * <li>If T is a Class itself, T itself is returned.
+     * <li>If T is a ParameterizedType, the raw type of the parameterized type is returned.
+     * <li>If T is a GenericArrayType, the returned type is the corresponding array class.
+     * For example: {@code List<Integer>[]} => {@code List[]}.
+     * <li>If T is a type variable or a wildcard type, the raw type of the first upper bound is
+     * returned. For example: {@code <X extends Foo>} => {@code Foo}.
+     * </ul>
+     *
+     * @return the raw type of {@code T}
+     */
+    @SuppressWarnings("unchecked")
+    public final Class<? super T> getRawType() {
+        return (Class<? super T>)getRawType(mType);
+    }
+
+    private static final Class<?> getRawType(Type type) {
+        if (type == null) {
+            throw new NullPointerException("type must not be null");
+        }
+
+        if (type instanceof Class<?>) {
+            return (Class<?>)type;
+        } else if (type instanceof ParameterizedType) {
+            return (Class<?>)(((ParameterizedType)type).getRawType());
+        } else if (type instanceof GenericArrayType) {
+            return getArrayClass(getRawType(((GenericArrayType)type).getGenericComponentType()));
+        } else if (type instanceof WildcardType) {
+            // Should be at most 1 upper bound, but treat it like an array for simplicity
+            return getRawType(((WildcardType) type).getUpperBounds());
+        } else if (type instanceof TypeVariable) {
+            throw new AssertionError("Type variables are not allowed in type references");
+        } else {
+            // Impossible
+            throw new AssertionError("Unhandled branch to get raw type for type " + type);
+        }
+    }
+
+    private static final Class<?> getRawType(Type[] types) {
+        if (types == null) {
+            return null;
+        }
+
+        for (Type type : types) {
+            Class<?> klass = getRawType(type);
+            if (klass !=  null) {
+                return klass;
+            }
+        }
+
+        return null;
+    }
+
+    private static final Class<?> getArrayClass(Class<?> componentType) {
+        return Array.newInstance(componentType, 0).getClass();
+    }
+
+    /**
+     * Get the component type, e.g. {@code T} from {@code T[]}.
+     *
+     * @return component type, or {@code null} if {@code T} is not an array
+     */
+    public TypeReference<?> getComponentType() {
+        Type componentType = getComponentType(mType);
+
+        return (componentType != null) ?
+                createSpecializedTypeReference(componentType) :
+                null;
+    }
+
+    private static Type getComponentType(Type type) {
+        checkNotNull(type, "type must not be null");
+
+        if (type instanceof Class<?>) {
+            return ((Class<?>) type).getComponentType();
+        } else if (type instanceof ParameterizedType) {
+            return null;
+        } else if (type instanceof GenericArrayType) {
+            return ((GenericArrayType)type).getGenericComponentType();
+        } else if (type instanceof WildcardType) {
+            // Should be at most 1 upper bound, but treat it like an array for simplicity
+            throw new UnsupportedOperationException("TODO: support wild card components");
+        } else if (type instanceof TypeVariable) {
+            throw new AssertionError("Type variables are not allowed in type references");
+        } else {
+            // Impossible
+            throw new AssertionError("Unhandled branch to get component type for type " + type);
+        }
+    }
+
+    /**
+     * Compare two objects for equality.
+     *
+     * <p>A TypeReference is only equal to another TypeReference if their captured type {@code T}
+     * is also equal.</p>
+     */
+    @Override
+    public boolean equals(Object o) {
+        // Note that this comparison could inaccurately return true when comparing types
+        // with nested type variables; therefore we ban type variables in the constructor.
+        return o instanceof TypeReference<?> && mType.equals(((TypeReference<?>)o).mType);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return mType.hashCode();
+    }
+
+    /**
+     * Check if the {@code type} contains a {@link TypeVariable} recursively.
+     *
+     * <p>Intuitively, a type variable is a type in a type expression that refers to a generic
+     * type which is not known at the definition of the expression (commonly seen when
+     * type parameters are used, e.g. {@code class Foo<T>}).</p>
+     *
+     * <p>See <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.4">
+     * http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.4</a>
+     * for a more formal definition of a type variable</p>.
+     *
+     * @param type a type object ({@code null} is allowed)
+     * @return {@code true} if there were nested type variables; {@code false} otherwise
+     */
+    public static boolean containsTypeVariable(Type type) {
+        if (type == null) {
+            // Trivially false
+            return false;
+        } else if (type instanceof TypeVariable<?>) {
+            /*
+             * T -> trivially true
+             */
+            return true;
+        } else if (type instanceof Class<?>) {
+            /*
+             * class Foo -> no type variable
+             * class Foo<T> - has a type variable
+             *
+             * This also covers the case of class Foo<T> extends ... / implements ...
+             * since everything on the right hand side would either include a type variable T
+             * or have no type variables.
+             */
+            Class<?> klass = (Class<?>)type;
+
+            // Empty array => class is not generic
+            if (klass.getTypeParameters().length != 0) {
+                return true;
+            } else {
+                // Does the outer class(es) contain any type variables?
+
+                /*
+                 * class Outer<T> {
+                 *   class Inner {
+                 *      T field;
+                 *   }
+                 * }
+                 *
+                 * In this case 'Inner' has no type parameters itself, but it still has a type
+                 * variable as part of the type definition.
+                 */
+                return containsTypeVariable(klass.getDeclaringClass());
+            }
+        } else if (type instanceof ParameterizedType) {
+            /*
+             * This is the "Foo<T1, T2, T3, ... Tn>" in the scope of a
+             *
+             *      // no type variables here, T1-Tn are known at this definition
+             *      class X extends Foo<T1, T2, T3, ... Tn>
+             *
+             *      // T1 is a type variable, T2-Tn are known at this definition
+             *      class X<T1> extends Foo<T1, T2, T3, ... Tn>
+             */
+            ParameterizedType p = (ParameterizedType) type;
+
+            // This needs to be recursively checked
+            for (Type arg : p.getActualTypeArguments()) {
+                if (containsTypeVariable(arg)) {
+                    return true;
+                }
+            }
+
+            return false;
+        } else if (type instanceof WildcardType) {
+            WildcardType wild = (WildcardType) type;
+
+            /*
+             * This is is the "?" inside of a
+             *
+             *       Foo<?> --> unbounded; trivially no type variables
+             *       Foo<? super T> --> lower bound; does T have a type variable?
+             *       Foo<? extends T> --> upper bound; does T have a type variable?
+             */
+
+            /*
+             *  According to JLS 4.5.1
+             *  (http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.5.1):
+             *
+             *  - More than 1 lower/upper bound is illegal
+             *  - Both a lower and upper bound is illegal
+             *
+             *  However, we use this 'array OR array' approach for readability
+             */
+            return containsTypeVariable(wild.getLowerBounds()) ||
+                    containsTypeVariable(wild.getUpperBounds());
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("TypeReference<");
+        toString(getType(), builder);
+        builder.append(">");
+
+        return builder.toString();
+    }
+
+    private static void toString(Type type, StringBuilder out) {
+        if (type == null) {
+            return;
+        } else if (type instanceof TypeVariable<?>) {
+            // T
+            out.append(((TypeVariable<?>)type).getName());
+        } else if (type instanceof Class<?>) {
+            Class<?> klass = (Class<?>)type;
+
+            out.append(klass.getName());
+            toString(klass.getTypeParameters(), out);
+        } else if (type instanceof ParameterizedType) {
+             // "Foo<T1, T2, T3, ... Tn>"
+            ParameterizedType p = (ParameterizedType) type;
+
+            out.append(((Class<?>)p.getRawType()).getName());
+            toString(p.getActualTypeArguments(), out);
+        } else if (type instanceof GenericArrayType) {
+            GenericArrayType gat = (GenericArrayType)type;
+
+            toString(gat.getGenericComponentType(), out);
+            out.append("[]");
+        } else { // WildcardType, BoundedType
+            // TODO:
+            out.append(type.toString());
+        }
+    }
+
+    private static void toString(Type[] types, StringBuilder out) {
+        if (types == null) {
+            return;
+        } else if (types.length == 0) {
+            return;
+        }
+
+        out.append("<");
+
+        for (int i = 0; i < types.length; ++i) {
+            toString(types[i], out);
+            if (i != types.length - 1) {
+                out.append(", ");
+            }
+        }
+
+        out.append(">");
+    }
+
+    /**
+     * Check if any of the elements in this array contained a type variable.
+     *
+     * <p>Empty and null arrays trivially have no type variables.</p>
+     *
+     * @param typeArray an array ({@code null} is ok) of types
+     * @return true if any elements contained a type variable; false otherwise
+     */
+    private static boolean containsTypeVariable(Type[] typeArray) {
+        if (typeArray == null) {
+            return false;
+        }
+
+        for (Type type : typeArray) {
+            if (containsTypeVariable(type)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 3e00250..2406cba 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1587,4 +1587,11 @@
         } catch (RemoteException e) {
         }
     }
+
+    /** {@hide} */
+    public void registerNetworkFactory(Messenger messenger) {
+        try {
+            mService.registerNetworkFactory(messenger);
+        } catch (RemoteException e) { }
+    }
 }
diff --git a/core/java/android/net/ConnectivityServiceProtocol.java b/core/java/android/net/ConnectivityServiceProtocol.java
new file mode 100644
index 0000000..34ba645
--- /dev/null
+++ b/core/java/android/net/ConnectivityServiceProtocol.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static com.android.internal.util.Protocol.BASE_CONNECTIVITY_SERVICE;
+
+/**
+ * Describes the Internal protocols used to communicate with ConnectivityService.
+ * @hide
+ */
+public class ConnectivityServiceProtocol {
+    private ConnectivityServiceProtocol() {}
+
+    private static final int BASE = BASE_CONNECTIVITY_SERVICE;
+
+    public static class NetworkFactoryProtocol {
+        private NetworkFactoryProtocol() {}
+        /**
+         * Pass a network request to the transport
+         * msg.obj = NetworkRequest
+         */
+        public static final int CMD_REQUEST_NETWORK = BASE;
+
+        /**
+         * Cancel a network request
+         * msg.obj = NetworkRequest
+         */
+        public static final int CMD_CANCEL_REQUEST = BASE + 1;
+    }
+
+    public static class NetworkAgentProtocol {
+        private NetworkAgentProtocol() {}
+    }
+}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index d53a856..b69797e 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -150,4 +150,6 @@
     void setProvisioningNotificationVisible(boolean visible, int networkType, in String extraInfo, in String url);
 
     void setAirplaneMode(boolean enable);
+
+    void registerNetworkFactory(in Messenger messenger);
 }
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index f82bc22..ac1289b 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -19,6 +19,8 @@
 import android.os.Parcelable;
 import android.os.Parcel;
 
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 
 /**
  * Identifies the Network.
@@ -36,6 +38,32 @@
         this.netId = that.netId;
     }
 
+    /**
+     * Operates the same as {@code InetAddress.getAllByName} except that host
+     * resolution is done on this network.
+     *
+     * @param host the hostname or literal IP string to be resolved.
+     * @return the array of addresses associated with the specified host.
+     * @throws UnknownHostException if the address lookup fails.
+     */
+    public InetAddress[] getAllByName(String host) throws UnknownHostException {
+        return InetAddress.getAllByNameOnNet(host, netId);
+    }
+
+    /**
+     * Operates the same as {@code InetAddress.getByName} except that host
+     * resolution is done on this network.
+     *
+     * @param host
+     *            the hostName to be resolved to an address or {@code null}.
+     * @return the {@code InetAddress} instance representing the host.
+     * @throws UnknownHostException
+     *             if the address lookup fails.
+     */
+    public InetAddress getByName(String host) throws UnknownHostException {
+        return InetAddress.getByNameOnNet(host, netId);
+    }
+
     // implement the Parcelable interface
     public int describeContents() {
         return 0;
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index daf0065..6a78c29 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -273,21 +273,19 @@
         String host = null;
         String port = null;
         String exclList = null;
-        String pacFileUrl = null;
+        Uri pacFileUrl = Uri.EMPTY;
         if (p != null) {
             host = p.getHost();
             port = Integer.toString(p.getPort());
             exclList = p.getExclusionListAsString();
-            if (p.getPacFileUrl() != null) {
-                pacFileUrl = p.getPacFileUrl().toString();
-            }
+            pacFileUrl = p.getPacFileUrl();
         }
         setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
     }
 
     /** @hide */
     public static final void setHttpProxySystemProperty(String host, String port, String exclList,
-            String pacFileUrl) {
+            Uri pacFileUrl) {
         if (exclList != null) exclList = exclList.replace(",", "|");
         if (false) Log.d(TAG, "setHttpProxySystemProperty :"+host+":"+port+" - "+exclList);
         if (host != null) {
@@ -311,7 +309,7 @@
             System.clearProperty("http.nonProxyHosts");
             System.clearProperty("https.nonProxyHosts");
         }
-        if (!TextUtils.isEmpty(pacFileUrl)) {
+        if (!Uri.EMPTY.equals(pacFileUrl)) {
             ProxySelector.setDefault(new PacProxySelector());
         } else {
             ProxySelector.setDefault(sDefaultProxySelector);
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index b40941f..991d9da 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -44,7 +44,7 @@
     private String mExclusionList;
     private String[] mParsedExclusionList;
 
-    private String mPacFileUrl;
+    private Uri mPacFileUrl;
     /**
      *@hide
      */
@@ -85,7 +85,7 @@
      * at the specified URL.
      */
     public static ProxyInfo buildPacProxy(Uri pacUri) {
-        return new ProxyInfo(pacUri.toString());
+        return new ProxyInfo(pacUri);
     }
 
     /**
@@ -96,6 +96,21 @@
         mHost = host;
         mPort = port;
         setExclusionList(exclList);
+        mPacFileUrl = Uri.EMPTY;
+    }
+
+    /**
+     * Create a ProxyProperties that points at a PAC URL.
+     * @hide
+     */
+    public ProxyInfo(Uri pacFileUrl) {
+        mHost = LOCAL_HOST;
+        mPort = LOCAL_PORT;
+        setExclusionList(LOCAL_EXCL_LIST);
+        if (pacFileUrl == null) {
+            throw new NullPointerException();
+        }
+        mPacFileUrl = pacFileUrl;
     }
 
     /**
@@ -106,17 +121,20 @@
         mHost = LOCAL_HOST;
         mPort = LOCAL_PORT;
         setExclusionList(LOCAL_EXCL_LIST);
-        mPacFileUrl = pacFileUrl;
+        mPacFileUrl = Uri.parse(pacFileUrl);
     }
 
     /**
      * Only used in PacManager after Local Proxy is bound.
      * @hide
      */
-    public ProxyInfo(String pacFileUrl, int localProxyPort) {
+    public ProxyInfo(Uri pacFileUrl, int localProxyPort) {
         mHost = LOCAL_HOST;
         mPort = localProxyPort;
         setExclusionList(LOCAL_EXCL_LIST);
+        if (pacFileUrl == null) {
+            throw new NullPointerException();
+        }
         mPacFileUrl = pacFileUrl;
     }
 
@@ -125,7 +143,7 @@
         mPort = port;
         mExclusionList = exclList;
         mParsedExclusionList = parsedExclList;
-        mPacFileUrl = null;
+        mPacFileUrl = Uri.EMPTY;
     }
 
     // copy constructor instead of clone
@@ -137,6 +155,9 @@
             mHost = source.getHost();
             mPort = source.getPort();
             mPacFileUrl = source.mPacFileUrl;
+            if (mPacFileUrl == null) {
+                mPacFileUrl = Uri.EMPTY;
+            }
             mExclusionList = source.getExclusionListAsString();
             mParsedExclusionList = source.mParsedExclusionList;
         }
@@ -158,10 +179,7 @@
      * no PAC script.
      */
     public Uri getPacFileUrl() {
-        if (TextUtils.isEmpty(mPacFileUrl)) {
-            return null;
-        }
-        return Uri.parse(mPacFileUrl);
+        return mPacFileUrl;
     }
 
     /**
@@ -210,7 +228,7 @@
      * @hide
      */
     public boolean isValid() {
-        if (!TextUtils.isEmpty(mPacFileUrl)) return true;
+        if (!Uri.EMPTY.equals(mPacFileUrl)) return true;
         return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost,
                                                 mPort == 0 ? "" : Integer.toString(mPort),
                                                 mExclusionList == null ? "" : mExclusionList);
@@ -234,7 +252,7 @@
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
-        if (mPacFileUrl != null) {
+        if (!Uri.EMPTY.equals(mPacFileUrl)) {
             sb.append("PAC Script: ");
             sb.append(mPacFileUrl);
         } else if (mHost != null) {
@@ -257,13 +275,15 @@
         ProxyInfo p = (ProxyInfo)o;
         // If PAC URL is present in either then they must be equal.
         // Other parameters will only be for fall back.
-        if (!TextUtils.isEmpty(mPacFileUrl)) {
+        if (!Uri.EMPTY.equals(mPacFileUrl)) {
             return mPacFileUrl.equals(p.getPacFileUrl()) && mPort == p.mPort;
         }
-        if (!TextUtils.isEmpty(p.mPacFileUrl)) {
+        if (!Uri.EMPTY.equals(p.mPacFileUrl)) {
             return false;
         }
-        if (mExclusionList != null && !mExclusionList.equals(p.getExclusionListAsString())) return false;
+        if (mExclusionList != null && !mExclusionList.equals(p.getExclusionListAsString())) {
+            return false;
+        }
         if (mHost != null && p.getHost() != null && mHost.equals(p.getHost()) == false) {
             return false;
         }
@@ -296,9 +316,9 @@
      * @hide
      */
     public void writeToParcel(Parcel dest, int flags) {
-        if (mPacFileUrl != null) {
+        if (!Uri.EMPTY.equals(mPacFileUrl)) {
             dest.writeByte((byte)1);
-            dest.writeString(mPacFileUrl);
+            mPacFileUrl.writeToParcel(dest, 0);
             dest.writeInt(mPort);
             return;
         } else {
@@ -325,7 +345,7 @@
                 String host = null;
                 int port = 0;
                 if (in.readByte() != 0) {
-                    String url = in.readString();
+                    Uri url = Uri.CREATOR.createFromParcel(in);
                     int localPort = in.readInt();
                     return new ProxyInfo(url, localPort);
                 }
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 1ca6b90..b11f7e4 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -515,9 +515,16 @@
         public static final int KITKAT = 19;
 
         /**
-         * Android 4.5: KitKat for watches, snacks on the run.
+         * Android 4.4W: KitKat for watches, snacks on the run.
+         *
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior:</p>
+         * <ul>
+         * <li>{@link android.app.AlertDialog} might not have a default background if the theme does
+         * not specify one.</li>
+         * </ul>
          */
-        public static final int KITKAT_WATCH = CUR_DEVELOPMENT; // STOPSHIP: update API level
+        public static final int KITKAT_WATCH = 20;
 
         /**
          * L!
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index f8d7c3e..5b2c8db 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -366,15 +366,6 @@
     }
 
     /**
-     * Returns true if the screen auto-brightness adjustment setting should
-     * be available in the UI.  This setting is experimental and disabled by default.
-     * @hide
-     */
-    public static boolean useScreenAutoBrightnessAdjustmentFeature() {
-        return SystemProperties.getBoolean("persist.power.useautobrightadj", false);
-    }
-
-    /**
      * Returns true if the twilight service should be used to adjust screen brightness
      * policy.  This setting is experimental and disabled by default.
      * @hide
diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java
index 4f996cd..9b929a3 100644
--- a/core/java/android/speech/tts/TtsEngines.java
+++ b/core/java/android/speech/tts/TtsEngines.java
@@ -307,6 +307,24 @@
     }
 
     /**
+     * True if a given TTS engine uses the default phone locale as a default locale. Attempts to
+     * read the value from {@link Settings.Secure#TTS_DEFAULT_LOCALE}, failing which the
+     * old style value from {@link Settings.Secure#TTS_DEFAULT_LANG} is read. If
+     * both these values are empty, this methods returns true.
+     *
+     * @param engineName the engine to return the locale for.
+     */
+    public boolean isLocaleSetToDefaultForEngine(String engineName) {
+        return (TextUtils.isEmpty(parseEnginePrefFromList(
+                    getString(mContext.getContentResolver(), Settings.Secure.TTS_DEFAULT_LOCALE),
+                    engineName)) &&
+                    TextUtils.isEmpty(
+                        Settings.Secure.getString(mContext.getContentResolver(),
+                        Settings.Secure.TTS_DEFAULT_LANG)));
+    }
+
+
+    /**
      * Parses a locale preference value delimited by {@link #LOCALE_DELIMITER}.
      * Varies from {@link String#split} in that it will always return an array
      * of length 3 with non null values.
diff --git a/core/java/android/transition/MoveImage.java b/core/java/android/transition/MoveImage.java
index 183cdd2..6f1b6f7 100644
--- a/core/java/android/transition/MoveImage.java
+++ b/core/java/android/transition/MoveImage.java
@@ -64,7 +64,13 @@
         if (!(view instanceof ImageView) || view.getVisibility() != View.VISIBLE) {
             return;
         }
+        ImageView imageView = (ImageView) view;
+        Drawable drawable = imageView.getDrawable();
+        if (drawable == null) {
+            return;
+        }
         Map<String, Object> values = transitionValues.values;
+        values.put(PROPNAME_DRAWABLE, drawable);
 
         ViewGroup parent = (ViewGroup) view.getParent();
         parent.getLocationInWindow(mTempLoc);
@@ -79,11 +85,9 @@
 
         Rect bounds = new Rect(left, top, right, bottom);
         values.put(PROPNAME_BOUNDS, bounds);
-        ImageView imageView = (ImageView) view;
         Matrix matrix = getMatrix(imageView);
         values.put(PROPNAME_MATRIX, matrix);
         values.put(PROPNAME_CLIP, findClip(imageView));
-        values.put(PROPNAME_DRAWABLE, imageView.getDrawable());
     }
 
     @Override
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index 49a0138..5a432dc 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -24,6 +24,7 @@
 import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 import android.util.SparseLongArray;
 import android.view.SurfaceView;
 import android.view.TextureView;
@@ -89,7 +90,8 @@
  * out-in behavior. Finally, note the use of the <code>targets</code> sub-tag, which
  * takes a set of {@link android.R.styleable#TransitionTarget target} tags, each
  * of which lists a specific <code>targetId</code>, <code>targetClass</code>,
- * <code>excludeId</code>, or <code>excludeClass</code>, which this transition acts upon.
+ * <code>targetViewName</code>, <code>excludeId</code>, <code>excludeClass</code>, or
+ * <code>excludeViewName</code>, which this transition acts upon.
  * Use of targets is optional, but can be used to either limit the time spent checking
  * attributes on unchanging views, or limiting the types of animations run on specific views.
  * In this case, we know that only the <code>grayscaleContainer</code> will be
@@ -113,10 +115,12 @@
     TimeInterpolator mInterpolator = null;
     ArrayList<Integer> mTargetIds = new ArrayList<Integer>();
     ArrayList<View> mTargets = new ArrayList<View>();
+    ArrayList<String> mTargetNames = null;
+    ArrayList<Class> mTargetTypes = null;
     ArrayList<Integer> mTargetIdExcludes = null;
     ArrayList<View> mTargetExcludes = null;
     ArrayList<Class> mTargetTypeExcludes = null;
-    ArrayList<Class> mTargetTypes = null;
+    ArrayList<String> mTargetNameExcludes = null;
     ArrayList<Integer> mTargetIdChildExcludes = null;
     ArrayList<View> mTargetChildExcludes = null;
     ArrayList<Class> mTargetTypeChildExcludes = null;
@@ -334,6 +338,133 @@
     }
 
     /**
+     * Match start/end values by View instance. Adds matched values to startValuesList
+     * and endValuesList and removes them from unmatchedStart and unmatchedEnd.
+     */
+    private void matchInstances(ArrayList<TransitionValues> startValuesList,
+            ArrayList<TransitionValues> endValuesList,
+            ArrayMap<View, TransitionValues> unmatchedStart,
+            ArrayMap<View, TransitionValues> unmatchedEnd) {
+        for (int i = unmatchedStart.size() - 1; i >= 0; i--) {
+            View view = unmatchedStart.keyAt(i);
+            TransitionValues end = unmatchedEnd.remove(view);
+            if (end != null) {
+                TransitionValues start = unmatchedStart.removeAt(i);
+                startValuesList.add(start);
+                endValuesList.add(end);
+            }
+        }
+    }
+
+    /**
+     * Match start/end values by Adapter item ID. Adds matched values to startValuesList
+     * and endValuesList and removes them from unmatchedStart and unmatchedEnd, using
+     * startItemIds and endItemIds as a guide for which Views have unique item IDs.
+     */
+    private void matchItemIds(ArrayList<TransitionValues> startValuesList,
+            ArrayList<TransitionValues> endValuesList,
+            ArrayMap<View, TransitionValues> unmatchedStart,
+            ArrayMap<View, TransitionValues> unmatchedEnd,
+            LongSparseArray<View> startItemIds, LongSparseArray<View> endItemIds) {
+        int numStartIds = startItemIds.size();
+        for (int i = 0; i < numStartIds; i++) {
+            View startView = startItemIds.valueAt(i);
+            if (startView != null) {
+                View endView = endItemIds.get(startItemIds.keyAt(i));
+                if (endView != null) {
+                    TransitionValues startValues = unmatchedStart.get(startView);
+                    TransitionValues endValues = unmatchedEnd.get(endView);
+                    if (startValues != null && endValues != null) {
+                        startValuesList.add(startValues);
+                        endValuesList.add(endValues);
+                        unmatchedStart.remove(startView);
+                        unmatchedEnd.remove(endView);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Match start/end values by Adapter view ID. Adds matched values to startValuesList
+     * and endValuesList and removes them from unmatchedStart and unmatchedEnd, using
+     * startIds and endIds as a guide for which Views have unique IDs.
+     */
+    private void matchIds(ArrayList<TransitionValues> startValuesList,
+            ArrayList<TransitionValues> endValuesList,
+            ArrayMap<View, TransitionValues> unmatchedStart,
+            ArrayMap<View, TransitionValues> unmatchedEnd,
+            SparseArray<View> startIds, SparseArray<View> endIds) {
+        int numStartIds = startIds.size();
+        for (int i = 0; i < numStartIds; i++) {
+            View startView = startIds.valueAt(i);
+            if (startView != null && isValidTarget(startView)) {
+                View endView = endIds.get(startIds.keyAt(i));
+                if (endView != null && isValidTarget(endView)) {
+                    TransitionValues startValues = unmatchedStart.get(startView);
+                    TransitionValues endValues = unmatchedEnd.get(endView);
+                    if (startValues != null && endValues != null) {
+                        startValuesList.add(startValues);
+                        endValuesList.add(endValues);
+                        unmatchedStart.remove(startView);
+                        unmatchedEnd.remove(endView);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Match start/end values by Adapter viewName. Adds matched values to startValuesList
+     * and endValuesList and removes them from unmatchedStart and unmatchedEnd, using
+     * startNames and endNames as a guide for which Views have unique viewNames.
+     */
+    private void matchNames(ArrayList<TransitionValues> startValuesList,
+            ArrayList<TransitionValues> endValuesList,
+            ArrayMap<View, TransitionValues> unmatchedStart,
+            ArrayMap<View, TransitionValues> unmatchedEnd,
+            ArrayMap<String, View> startNames, ArrayMap<String, View> endNames) {
+        int numStartNames = startNames.size();
+        for (int i = 0; i < numStartNames; i++) {
+            View startView = startNames.valueAt(i);
+            if (startView != null && isValidTarget(startView)) {
+                View endView = endNames.get(startNames.keyAt(i));
+                if (endView != null && isValidTarget(endView)) {
+                    TransitionValues startValues = unmatchedStart.get(startView);
+                    TransitionValues endValues = unmatchedEnd.get(endView);
+                    if (startValues != null && endValues != null) {
+                        startValuesList.add(startValues);
+                        endValuesList.add(endValues);
+                        unmatchedStart.remove(startView);
+                        unmatchedEnd.remove(endView);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds all values from unmatchedStart and unmatchedEnd to startValuesList and endValuesList,
+     * assuming that there is no match between values in the list.
+     */
+    private void addUnmatched(ArrayList<TransitionValues> startValuesList,
+            ArrayList<TransitionValues> endValuesList,
+            ArrayMap<View, TransitionValues> unmatchedStart,
+            ArrayMap<View, TransitionValues> unmatchedEnd) {
+        // Views that only exist in the start Scene
+        for (int i = 0; i < unmatchedStart.size(); i++) {
+            startValuesList.add(unmatchedStart.valueAt(i));
+            endValuesList.add(null);
+        }
+
+        // Views that only exist in the end Scene
+        for (int i = 0; i < unmatchedEnd.size(); i++) {
+            endValuesList.add(unmatchedEnd.valueAt(i));
+            startValuesList.add(null);
+        }
+    }
+
+    /**
      * This method, essentially a wrapper around all calls to createAnimator for all
      * possible target views, is called with the entire set of start/end
      * values. The implementation in Transition iterates through these lists
@@ -349,76 +480,22 @@
         if (DBG) {
             Log.d(LOG_TAG, "createAnimators() for " + this);
         }
-        ArrayMap<View, TransitionValues> endCopy =
+        ArrayMap<View, TransitionValues> unmatchedStart =
+                new ArrayMap<View, TransitionValues>(startValues.viewValues);
+        ArrayMap<View, TransitionValues> unmatchedEnd =
                 new ArrayMap<View, TransitionValues>(endValues.viewValues);
-        SparseArray<TransitionValues> endIdCopy = endValues.idValues.clone();
-        LongSparseArray<TransitionValues> endItemIdCopy = endValues.itemIdValues.clone();
-        // Walk through the start values, playing everything we find
-        // Remove from the end set as we go
+
         ArrayList<TransitionValues> startValuesList = new ArrayList<TransitionValues>();
         ArrayList<TransitionValues> endValuesList = new ArrayList<TransitionValues>();
-        for (View view : startValues.viewValues.keySet()) {
-            TransitionValues start = null;
-            TransitionValues end = null;
-            boolean isInListView = false;
-            if (view.getParent() instanceof ListView) {
-                isInListView = true;
-            }
-            if (!isInListView) {
-                int id = view.getId();
-                start = startValues.viewValues.get(view);
-                end = endValues.viewValues.get(view);
-                if (end != null) {
-                    endCopy.remove(view);
-                } else if (id != View.NO_ID) {
-                    end = endIdCopy.get(id);
-                    if (end == null || startValues.viewValues.containsKey(end.view)) {
-                        end = null;
-                        id = View.NO_ID;
-                    } else {
-                        endCopy.remove(end.view);
-                    }
-                }
-                endIdCopy.remove(id);
-                if (isValidTarget(view, id)) {
-                    startValuesList.add(start);
-                    endValuesList.add(end);
-                }
-            } else {
-                ListView parent = (ListView) view.getParent();
-                if (parent.getAdapter().hasStableIds()) {
-                    int position = parent.getPositionForView(view);
-                    long itemId = parent.getItemIdAtPosition(position);
-                    start = startValues.itemIdValues.get(itemId);
-                    endItemIdCopy.remove(itemId);
-                    // TODO: deal with targetIDs for itemIDs for ListView items
-                    startValuesList.add(start);
-                    endValuesList.add(end);
-                }
-            }
-        }
-        int startItemIdCopySize = startValues.itemIdValues.size();
-        for (int i = 0; i < startItemIdCopySize; ++i) {
-            long id = startValues.itemIdValues.keyAt(i);
-            if (isValidTarget(null, id)) {
-                TransitionValues start = startValues.itemIdValues.get(id);
-                TransitionValues end = endValues.itemIdValues.get(id);
-                endItemIdCopy.remove(id);
-                startValuesList.add(start);
-                endValuesList.add(end);
-            }
-        }
-        // Now walk through the remains of the end set
-        // We've already matched everything from start to end, everything else doesn't match.
-        for (View view : endCopy.keySet()) {
-            int id = view.getId();
-            if (isValidTarget(view, id)) {
-                TransitionValues start = null;
-                TransitionValues end = endCopy.get(view);
-                startValuesList.add(start);
-                endValuesList.add(end);
-            }
-        }
+        matchNames(startValuesList, endValuesList, unmatchedStart, unmatchedEnd,
+                startValues.nameValues, endValues.nameValues);
+        matchInstances(startValuesList, endValuesList, unmatchedStart, unmatchedEnd);
+        matchIds(startValuesList, endValuesList, unmatchedStart, unmatchedEnd,
+                startValues.idValues, endValues.idValues);
+        matchItemIds(startValuesList, endValuesList, unmatchedStart, unmatchedEnd,
+                startValues.itemIdValues, endValues.itemIdValues);
+        addUnmatched(startValuesList, endValuesList, unmatchedStart, unmatchedEnd);
+
         ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
         long minStartDelay = Long.MAX_VALUE;
         int minAnimator = mAnimators.size();
@@ -520,7 +597,8 @@
      * is not checked (this is in the case of ListView items, where the
      * views are ignored and only the ids are used).
      */
-    boolean isValidTarget(View target, long targetId) {
+    boolean isValidTarget(View target) {
+        int targetId = target.getId();
         if (mTargetIdExcludes != null && mTargetIdExcludes.contains(targetId)) {
             return false;
         }
@@ -536,10 +614,20 @@
                 }
             }
         }
-        if (mTargetIds.size() == 0 && mTargets.size() == 0 && mTargetTypes == null) {
+        if (mTargetNameExcludes != null && target != null && target.getViewName() != null) {
+            if (mTargetNameExcludes.contains(target.getViewName())) {
+                return false;
+            }
+        }
+        if (mTargetIds.size() == 0 && mTargets.size() == 0 &&
+                (mTargetTypes == null || mTargetTypes.isEmpty() &&
+                (mTargetNames == null || mTargetNames.isEmpty()))) {
             return true;
         }
-        if (mTargetIds.contains((int) targetId) || mTargets.contains(target)) {
+        if (mTargetIds.contains(targetId) || mTargets.contains(target)) {
+            return true;
+        }
+        if (mTargetNames != null && mTargetNames.contains(target.getViewName())) {
             return true;
         }
         if (mTargetTypes != null) {
@@ -690,6 +778,33 @@
     }
 
     /**
+     * Adds the viewName of a target view that this Transition is interested in
+     * animating. By default, there are no targetNames, and a Transition will
+     * listen for changes on every view in the hierarchy below the sceneRoot
+     * of the Scene being transitioned into. Setting targetNames constrains
+     * the Transition to only listen for, and act on, views with these viewNames.
+     * Views with different viewNames, or no viewName whatsoever, will be ignored.
+     *
+     * <p>Note that viewNames should be unique within the view hierarchy.</p>
+     *
+     * @see android.view.View#getViewName()
+     * @param targetName The viewName of a target view, must be non-null.
+     * @return The Transition to which the target viewName is added.
+     * Returning the same object makes it easier to chain calls during
+     * construction, such as
+     * <code>transitionSet.addTransitions(new Fade()).addTarget(someName);</code>
+     */
+    public Transition addTarget(String targetName) {
+        if (targetName != null) {
+            if (mTargetNames != null) {
+                mTargetNames = new ArrayList<String>();
+            }
+            mTargetNames.add(targetName);
+        }
+        return this;
+    }
+
+    /**
      * Adds the Class of a target view that this Transition is interested in
      * animating. By default, there are no targetTypes, and a Transition will
      * listen for changes on every view in the hierarchy below the sceneRoot
@@ -712,10 +827,12 @@
      * <code>transitionSet.addTransitions(new Fade()).addTarget(ImageView.class);</code>
      */
     public Transition addTarget(Class targetType) {
-        if (mTargetTypes == null) {
-            mTargetTypes = new ArrayList<Class>();
+        if (targetType != null) {
+            if (mTargetTypes == null) {
+                mTargetTypes = new ArrayList<Class>();
+            }
+            mTargetTypes.add(targetType);
         }
-        mTargetTypes.add(targetType);
         return this;
     }
 
@@ -737,6 +854,23 @@
     }
 
     /**
+     * Removes the given targetName from the list of viewNames that this Transition
+     * is interested in animating.
+     *
+     * @param targetName The viewName of a target view, must not be null.
+     * @return The Transition from which the targetName is removed.
+     * Returning the same object makes it easier to chain calls during
+     * construction, such as
+     * <code>transitionSet.addTransitions(new Fade()).removeTargetName(someName);</code>
+     */
+    public Transition removeTarget(String targetName) {
+        if (targetName != null && mTargetNames != null) {
+            mTargetNames.remove(targetName);
+        }
+        return this;
+    }
+
+    /**
      * Whether to add the given id to the list of target ids to exclude from this
      * transition. The <code>exclude</code> parameter specifies whether the target
      * should be added to or removed from the excluded list.
@@ -758,7 +892,35 @@
      * @return This transition object.
      */
     public Transition excludeTarget(int targetId, boolean exclude) {
-        mTargetIdExcludes = excludeId(mTargetIdExcludes, targetId, exclude);
+        if (targetId >= 0) {
+            mTargetIdExcludes = excludeObject(mTargetIdExcludes, targetId, exclude);
+        }
+        return this;
+    }
+
+    /**
+     * Whether to add the given viewName to the list of target viewNames to exclude from this
+     * transition. The <code>exclude</code> parameter specifies whether the target
+     * should be added to or removed from the excluded list.
+     *
+     * <p>Excluding targets is a general mechanism for allowing transitions to run on
+     * a view hierarchy while skipping target views that should not be part of
+     * the transition. For example, you may want to avoid animating children
+     * of a specific ListView or Spinner. Views can be excluded by their
+     * id, their instance reference, their viewName, or by the Class of that view
+     * (eg, {@link Spinner}).</p>
+     *
+     * @see #excludeTarget(View, boolean)
+     * @see #excludeTarget(int, boolean)
+     * @see #excludeTarget(Class, boolean)
+     *
+     * @param targetViewName The name of a target to ignore when running this transition.
+     * @param exclude Whether to add the target to or remove the target from the
+     * current list of excluded targets.
+     * @return This transition object.
+     */
+    public Transition excludeTarget(String targetViewName, boolean exclude) {
+        mTargetNameExcludes = excludeObject(mTargetNameExcludes, targetViewName, exclude);
         return this;
     }
 
@@ -788,23 +950,10 @@
      * @return This transition object.
      */
     public Transition excludeChildren(int targetId, boolean exclude) {
-        mTargetIdChildExcludes = excludeId(mTargetIdChildExcludes, targetId, exclude);
-        return this;
-    }
-
-    /**
-     * Utility method to manage the boilerplate code that is the same whether we
-     * are excluding targets or their children.
-     */
-    private ArrayList<Integer> excludeId(ArrayList<Integer> list, int targetId, boolean exclude) {
-        if (targetId > 0) {
-            if (exclude) {
-                list = ArrayListManager.add(list, targetId);
-            } else {
-                list = ArrayListManager.remove(list, targetId);
-            }
+        if (targetId >= 0) {
+            mTargetIdChildExcludes = excludeObject(mTargetIdChildExcludes, targetId, exclude);
         }
-        return list;
+        return this;
     }
 
     /**
@@ -829,7 +978,7 @@
      * @return This transition object.
      */
     public Transition excludeTarget(View target, boolean exclude) {
-        mTargetExcludes = excludeView(mTargetExcludes, target, exclude);
+        mTargetExcludes = excludeObject(mTargetExcludes, target, exclude);
         return this;
     }
 
@@ -855,7 +1004,7 @@
      * @return This transition object.
      */
     public Transition excludeChildren(View target, boolean exclude) {
-        mTargetChildExcludes = excludeView(mTargetChildExcludes, target, exclude);
+        mTargetChildExcludes = excludeObject(mTargetChildExcludes, target, exclude);
         return this;
     }
 
@@ -863,7 +1012,7 @@
      * Utility method to manage the boilerplate code that is the same whether we
      * are excluding targets or their children.
      */
-    private ArrayList<View> excludeView(ArrayList<View> list, View target, boolean exclude) {
+    private static <T> ArrayList<T> excludeObject(ArrayList<T> list, T target, boolean exclude) {
         if (target != null) {
             if (exclude) {
                 list = ArrayListManager.add(list, target);
@@ -896,7 +1045,7 @@
      * @return This transition object.
      */
     public Transition excludeTarget(Class type, boolean exclude) {
-        mTargetTypeExcludes = excludeType(mTargetTypeExcludes, type, exclude);
+        mTargetTypeExcludes = excludeObject(mTargetTypeExcludes, type, exclude);
         return this;
     }
 
@@ -923,26 +1072,11 @@
      * @return This transition object.
      */
     public Transition excludeChildren(Class type, boolean exclude) {
-        mTargetTypeChildExcludes = excludeType(mTargetTypeChildExcludes, type, exclude);
+        mTargetTypeChildExcludes = excludeObject(mTargetTypeChildExcludes, type, exclude);
         return this;
     }
 
     /**
-     * Utility method to manage the boilerplate code that is the same whether we
-     * are excluding targets or their children.
-     */
-    private ArrayList<Class> excludeType(ArrayList<Class> list, Class type, boolean exclude) {
-        if (type != null) {
-            if (exclude) {
-                list = ArrayListManager.add(list, type);
-            } else {
-                list = ArrayListManager.remove(list, type);
-            }
-        }
-        return list;
-    }
-
-    /**
      * Sets the target view instances that this Transition is interested in
      * animating. By default, there are no targets, and a Transition will
      * listen for changes on every view in the hierarchy below the sceneRoot
@@ -991,9 +1125,27 @@
     }
 
     /**
-     * Returns the array of target IDs that this transition limits itself to
-     * tracking and animating. If the array is null for both this method and
-     * {@link #getTargets()}, then this transition is
+     * Removes the given target from the list of targets that this Transition
+     * is interested in animating.
+     *
+     * @param target The type of the target view, must be non-null.
+     * @return Transition The Transition from which the target is removed.
+     * Returning the same object makes it easier to chain calls during
+     * construction, such as
+     * <code>transitionSet.addTransitions(new Fade()).removeTarget(someType);</code>
+     */
+    public Transition removeTarget(Class target) {
+        if (target != null) {
+            mTargetTypes.remove(target);
+        }
+        return this;
+    }
+
+    /**
+     * Returns the list of target IDs that this transition limits itself to
+     * tracking and animating. If the list is null or empty for
+     * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and
+     * {@link #getTargetTypes()} then this transition is
      * not limited to specific views, and will handle changes to any views
      * in the hierarchy of a scene change.
      *
@@ -1004,9 +1156,10 @@
     }
 
     /**
-     * Returns the array of target views that this transition limits itself to
-     * tracking and animating. If the array is null for both this method and
-     * {@link #getTargetIds()}, then this transition is
+     * Returns the list of target views that this transition limits itself to
+     * tracking and animating. If the list is null or empty for
+     * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and
+     * {@link #getTargetTypes()} then this transition is
      * not limited to specific views, and will handle changes to any views
      * in the hierarchy of a scene change.
      *
@@ -1017,6 +1170,34 @@
     }
 
     /**
+     * Returns the list of target viewNames that this transition limits itself to
+     * tracking and animating. If the list is null or empty for
+     * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and
+     * {@link #getTargetTypes()} then this transition is
+     * not limited to specific views, and will handle changes to any views
+     * in the hierarchy of a scene change.
+     *
+     * @return the list of target viewNames
+     */
+    public List<String> getTargetViewNames() {
+        return mTargetNames;
+    }
+
+    /**
+     * Returns the list of target viewNames that this transition limits itself to
+     * tracking and animating. If the list is null or empty for
+     * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and
+     * {@link #getTargetTypes()} then this transition is
+     * not limited to specific views, and will handle changes to any views
+     * in the hierarchy of a scene change.
+     *
+     * @return the list of target Types
+     */
+    public List<Class> getTargetTypes() {
+        return mTargetTypes;
+    }
+
+    /**
      * Recursive method that captures values for the given view and the
      * hierarchy underneath it.
      * @param sceneRoot The root of the view hierarchy being captured
@@ -1025,52 +1206,42 @@
      */
     void captureValues(ViewGroup sceneRoot, boolean start) {
         clearValues(start);
-        if (mTargetIds.size() > 0 || mTargets.size() > 0) {
-            if (mTargetIds.size() > 0) {
-                for (int i = 0; i < mTargetIds.size(); ++i) {
-                    int id = mTargetIds.get(i);
-                    View view = sceneRoot.findViewById(id);
-                    if (view != null) {
-                        TransitionValues values = new TransitionValues();
-                        values.view = view;
-                        if (start) {
-                            captureStartValues(values);
-                        } else {
-                            captureEndValues(values);
-                        }
-                        capturePropagationValues(values);
-                        if (start) {
-                            mStartValues.viewValues.put(view, values);
-                            if (id >= 0) {
-                                mStartValues.idValues.put(id, values);
-                            }
-                        } else {
-                            mEndValues.viewValues.put(view, values);
-                            if (id >= 0) {
-                                mEndValues.idValues.put(id, values);
-                            }
-                        }
+        if ((mTargetIds.size() > 0 || mTargets.size() > 0)
+                && (mTargetNames == null || mTargetNames.isEmpty())
+                && (mTargetTypes == null || mTargetTypes.isEmpty())) {
+            for (int i = 0; i < mTargetIds.size(); ++i) {
+                int id = mTargetIds.get(i);
+                View view = sceneRoot.findViewById(id);
+                if (view != null) {
+                    TransitionValues values = new TransitionValues();
+                    values.view = view;
+                    if (start) {
+                        captureStartValues(values);
+                    } else {
+                        captureEndValues(values);
+                    }
+                    capturePropagationValues(values);
+                    if (start) {
+                        addViewValues(mStartValues, view, values);
+                    } else {
+                        addViewValues(mEndValues, view, values);
                     }
                 }
             }
-            if (mTargets.size() > 0) {
-                for (int i = 0; i < mTargets.size(); ++i) {
-                    View view = mTargets.get(i);
-                    if (view != null) {
-                        TransitionValues values = new TransitionValues();
-                        values.view = view;
-                        if (start) {
-                            captureStartValues(values);
-                        } else {
-                            captureEndValues(values);
-                        }
-                        capturePropagationValues(values);
-                        if (start) {
-                            mStartValues.viewValues.put(view, values);
-                        } else {
-                            mEndValues.viewValues.put(view, values);
-                        }
-                    }
+            for (int i = 0; i < mTargets.size(); ++i) {
+                View view = mTargets.get(i);
+                TransitionValues values = new TransitionValues();
+                values.view = view;
+                if (start) {
+                    captureStartValues(values);
+                } else {
+                    captureEndValues(values);
+                }
+                capturePropagationValues(values);
+                if (start) {
+                    mStartValues.viewValues.put(view, values);
+                } else {
+                    mEndValues.viewValues.put(view, values);
                 }
             }
         } else {
@@ -1078,6 +1249,47 @@
         }
     }
 
+    static void addViewValues(TransitionValuesMaps transitionValuesMaps,
+            View view, TransitionValues transitionValues) {
+        transitionValuesMaps.viewValues.put(view, transitionValues);
+        int id = view.getId();
+        if (id >= 0) {
+            if (transitionValuesMaps.idValues.indexOfKey(id) >= 0) {
+                // Duplicate IDs cannot match by ID.
+                transitionValuesMaps.idValues.put(id, null);
+            } else {
+                transitionValuesMaps.idValues.put(id, view);
+            }
+        }
+        String name = view.getViewName();
+        if (name != null) {
+            if (transitionValuesMaps.nameValues.containsKey(name)) {
+                // Duplicate viewNames: cannot match by viewName.
+                transitionValuesMaps.nameValues.put(name, null);
+            } else {
+                transitionValuesMaps.nameValues.put(name, view);
+            }
+        }
+        if (view.getParent() instanceof ListView) {
+            ListView listview = (ListView) view.getParent();
+            if (listview.getAdapter().hasStableIds()) {
+                int position = listview.getPositionForView(view);
+                long itemId = listview.getItemIdAtPosition(position);
+                if (transitionValuesMaps.itemIdValues.indexOfKey(itemId) >= 0) {
+                    // Duplicate item IDs: cannot match by item ID.
+                    View alreadyMatched = transitionValuesMaps.itemIdValues.get(itemId);
+                    if (alreadyMatched != null) {
+                        alreadyMatched.setHasTransientState(false);
+                        transitionValuesMaps.itemIdValues.put(itemId, null);
+                    }
+                } else {
+                    view.setHasTransientState(true);
+                    transitionValuesMaps.itemIdValues.put(itemId, view);
+                }
+            }
+        }
+    }
+
     /**
      * Clear valuesMaps for specified start/end state
      *
@@ -1109,24 +1321,7 @@
         if (view == null) {
             return;
         }
-        boolean isListViewItem = false;
-        if (view.getParent() instanceof ListView) {
-            isListViewItem = true;
-        }
-        if (isListViewItem && !((ListView) view.getParent()).getAdapter().hasStableIds()) {
-            // ignore listview children unless we can track them with stable IDs
-            return;
-        }
-        int id = View.NO_ID;
-        long itemId = View.NO_ID;
-        if (!isListViewItem) {
-            id = view.getId();
-        } else {
-            ListView listview = (ListView) view.getParent();
-            int position = listview.getPositionForView(view);
-            itemId = listview.getItemIdAtPosition(position);
-            view.setHasTransientState(true);
-        }
+        int id = view.getId();
         if (mTargetIdExcludes != null && mTargetIdExcludes.contains(id)) {
             return;
         }
@@ -1151,23 +1346,9 @@
             }
             capturePropagationValues(values);
             if (start) {
-                if (!isListViewItem) {
-                    mStartValues.viewValues.put(view, values);
-                    if (id >= 0) {
-                        mStartValues.idValues.put((int) id, values);
-                    }
-                } else {
-                    mStartValues.itemIdValues.put(itemId, values);
-                }
+                addViewValues(mStartValues, view, values);
             } else {
-                if (!isListViewItem) {
-                    mEndValues.viewValues.put(view, values);
-                    if (id >= 0) {
-                        mEndValues.idValues.put((int) id, values);
-                    }
-                } else {
-                    mEndValues.itemIdValues.put(itemId, values);
-                }
+                addViewValues(mEndValues, view, values);
             }
         }
         if (view instanceof ViewGroup) {
@@ -1178,7 +1359,7 @@
             if (mTargetChildExcludes != null && mTargetChildExcludes.contains(view)) {
                 return;
             }
-            if (mTargetTypeChildExcludes != null && view != null) {
+            if (mTargetTypeChildExcludes != null) {
                 int numTypes = mTargetTypeChildExcludes.size();
                 for (int i = 0; i < numTypes; ++i) {
                     if (mTargetTypeChildExcludes.get(i).isInstance(view)) {
@@ -1204,22 +1385,7 @@
             return mParent.getTransitionValues(view, start);
         }
         TransitionValuesMaps valuesMaps = start ? mStartValues : mEndValues;
-        TransitionValues values = valuesMaps.viewValues.get(view);
-        if (values == null) {
-            int id = view.getId();
-            if (id >= 0) {
-                values = valuesMaps.idValues.get(id);
-            }
-            if (values == null && view.getParent() instanceof ListView) {
-                ListView listview = (ListView) view.getParent();
-                int position = listview.getPositionForView(view);
-                long itemId = listview.getItemIdAtPosition(position);
-                values = valuesMaps.itemIdValues.get(itemId);
-            }
-            // TODO: Doesn't handle the case where a view was parented to a
-            // ListView (with an itemId), but no longer is
-        }
-        return values;
+        return valuesMaps.viewValues.get(view);
     }
 
     /**
@@ -1303,11 +1469,7 @@
                     boolean cancel = false;
                     TransitionValues oldValues = oldInfo.values;
                     View oldView = oldInfo.view;
-                    TransitionValues newValues = mEndValues.viewValues != null ?
-                            mEndValues.viewValues.get(oldView) : null;
-                    if (newValues == null) {
-                        newValues = mEndValues.idValues.get(oldView.getId());
-                    }
+                    TransitionValues newValues = mEndValues.viewValues.get(oldView);
                     if (oldValues != null) {
                         // if oldValues null, then transition didn't care to stash values,
                         // and won't get canceled
@@ -1429,17 +1591,15 @@
                 }
             }
             for (int i = 0; i < mStartValues.itemIdValues.size(); ++i) {
-                TransitionValues tv = mStartValues.itemIdValues.valueAt(i);
-                View v = tv.view;
-                if (v.hasTransientState()) {
-                    v.setHasTransientState(false);
+                View view = mStartValues.itemIdValues.valueAt(i);
+                if (view != null) {
+                    view.setHasTransientState(false);
                 }
             }
             for (int i = 0; i < mEndValues.itemIdValues.size(); ++i) {
-                TransitionValues tv = mEndValues.itemIdValues.valueAt(i);
-                View v = tv.view;
-                if (v.hasTransientState()) {
-                    v.setHasTransientState(false);
+                View view = mEndValues.itemIdValues.valueAt(i);
+                if (view != null) {
+                    view.setHasTransientState(false);
                 }
             }
             mEnded = true;
diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java
index a5e960a..04f8672 100644
--- a/core/java/android/transition/TransitionInflater.java
+++ b/core/java/android/transition/TransitionInflater.java
@@ -229,11 +229,20 @@
                         com.android.internal.R.styleable.TransitionTarget);
                 int id = a.getResourceId(
                         com.android.internal.R.styleable.TransitionTarget_targetId, -1);
+                String viewName;
                 if (id >= 0) {
                     transition.addTarget(id);
                 } else if ((id = a.getResourceId(
                         com.android.internal.R.styleable.TransitionTarget_excludeId, -1)) >= 0) {
                     transition.excludeTarget(id, true);
+                } else if ((viewName = a.getString(
+                            com.android.internal.R.styleable.TransitionTarget_targetViewName))
+                        != null) {
+                    transition.addTarget(viewName);
+                } else if ((viewName = a.getString(
+                        com.android.internal.R.styleable.TransitionTarget_excludeViewName))
+                        != null) {
+                    transition.excludeTarget(viewName, true);
                 } else {
                     String className = a.getString(
                             com.android.internal.R.styleable.TransitionTarget_excludeClass);
diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java
index 9081234..698b563 100644
--- a/core/java/android/transition/TransitionSet.java
+++ b/core/java/android/transition/TransitionSet.java
@@ -272,24 +272,8 @@
         int numValues = values.viewValues.size();
         for (int i = 0; i < numValues; i++) {
             View view = values.viewValues.keyAt(i);
-            if (isValidTarget(view, view.getId())) {
-                included.viewValues.put(view, values.viewValues.valueAt(i));
-            }
-        }
-        numValues = values.idValues.size();
-        for (int i = 0; i < numValues; i++) {
-            int id = values.idValues.keyAt(i);
-            TransitionValues transitionValues = values.idValues.valueAt(i);
-            if (isValidTarget(transitionValues.view, id)) {
-                included.idValues.put(id, transitionValues);
-            }
-        }
-        numValues = values.itemIdValues.size();
-        for (int i = 0; i < numValues; i++) {
-            long id = values.itemIdValues.keyAt(i);
-            TransitionValues transitionValues = values.itemIdValues.valueAt(i);
-            if (isValidTarget(transitionValues.view, id)) {
-                included.itemIdValues.put(id, transitionValues);
+            if (isValidTarget(view)) {
+                addViewValues(included, view, values.viewValues.valueAt(i));
             }
         }
         return included;
@@ -328,10 +312,9 @@
 
     @Override
     public void captureStartValues(TransitionValues transitionValues) {
-        int targetId = transitionValues.view.getId();
-        if (isValidTarget(transitionValues.view, targetId)) {
+        if (isValidTarget(transitionValues.view)) {
             for (Transition childTransition : mTransitions) {
-                if (childTransition.isValidTarget(transitionValues.view, targetId)) {
+                if (childTransition.isValidTarget(transitionValues.view)) {
                     childTransition.captureStartValues(transitionValues);
                 }
             }
@@ -340,10 +323,9 @@
 
     @Override
     public void captureEndValues(TransitionValues transitionValues) {
-        int targetId = transitionValues.view.getId();
-        if (isValidTarget(transitionValues.view, targetId)) {
+        if (isValidTarget(transitionValues.view)) {
             for (Transition childTransition : mTransitions) {
-                if (childTransition.isValidTarget(transitionValues.view, targetId)) {
+                if (childTransition.isValidTarget(transitionValues.view)) {
                     childTransition.captureEndValues(transitionValues);
                 }
             }
diff --git a/core/java/android/transition/TransitionValuesMaps.java b/core/java/android/transition/TransitionValuesMaps.java
index 131596b..6d5700a 100644
--- a/core/java/android/transition/TransitionValuesMaps.java
+++ b/core/java/android/transition/TransitionValuesMaps.java
@@ -24,7 +24,7 @@
 class TransitionValuesMaps {
     ArrayMap<View, TransitionValues> viewValues =
             new ArrayMap<View, TransitionValues>();
-    SparseArray<TransitionValues> idValues = new SparseArray<TransitionValues>();
-    LongSparseArray<TransitionValues> itemIdValues =
-            new LongSparseArray<TransitionValues>();
+    SparseArray<View> idValues = new SparseArray<View>();
+    LongSparseArray<View> itemIdValues = new LongSparseArray<View>();
+    ArrayMap<String, View> nameValues = new ArrayMap<String, View>();
 }
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index 6e6496c..0f7638b 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -162,26 +162,15 @@
     public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
             TransitionValues endValues) {
         VisibilityInfo visInfo = getVisibilityChangeInfo(startValues, endValues);
-        if (visInfo.visibilityChange) {
-            // Only transition views that are either targets of this transition
-            // or whose parent hierarchies remain stable between scenes
-            boolean isTarget = false;
-            if (mTargets.size() > 0 || mTargetIds.size() > 0) {
-                View startView = startValues != null ? startValues.view : null;
-                View endView = endValues != null ? endValues.view : null;
-                int startId = startView != null ? startView.getId() : -1;
-                int endId = endView != null ? endView.getId() : -1;
-                isTarget = isValidTarget(startView, startId) || isValidTarget(endView, endId);
-            }
-            if (isTarget || ((visInfo.startParent != null || visInfo.endParent != null))) {
-                if (visInfo.fadeIn) {
-                    return onAppear(sceneRoot, startValues, visInfo.startVisibility,
-                            endValues, visInfo.endVisibility);
-                } else {
-                    return onDisappear(sceneRoot, startValues, visInfo.startVisibility,
-                            endValues, visInfo.endVisibility
-                    );
-                }
+        if (visInfo.visibilityChange
+                && (visInfo.startParent != null || visInfo.endParent != null)) {
+            if (visInfo.fadeIn) {
+                return onAppear(sceneRoot, startValues, visInfo.startVisibility,
+                        endValues, visInfo.endVisibility);
+            } else {
+                return onDisappear(sceneRoot, startValues, visInfo.startVisibility,
+                        endValues, visInfo.endVisibility
+                );
             }
         }
         return null;
diff --git a/core/java/android/tv/TvInputService.java b/core/java/android/tv/TvInputService.java
index 1d6298d..eeb738d 100644
--- a/core/java/android/tv/TvInputService.java
+++ b/core/java/android/tv/TvInputService.java
@@ -489,7 +489,7 @@
                     }
                 }
             }
-            if (mOverlayView == null) {
+            if (mOverlayView == null || !mOverlayView.isAttachedToWindow()) {
                 return Session.DISPATCH_NOT_HANDLED;
             }
             if (!mOverlayView.hasWindowFocus()) {
diff --git a/core/java/android/tv/TvView.java b/core/java/android/tv/TvView.java
index 80501e8..7721575 100644
--- a/core/java/android/tv/TvView.java
+++ b/core/java/android/tv/TvView.java
@@ -31,6 +31,7 @@
 import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
+import android.view.ViewRootImpl;
 
 /**
  * View playing TV
@@ -89,7 +90,10 @@
             if (dispatchUnhandledInputEvent(event)) {
                 return;
             }
-            getViewRootImpl().dispatchUnhandledInputEvent(event);
+            ViewRootImpl viewRootImpl = getViewRootImpl();
+            if (viewRootImpl != null) {
+                viewRootImpl.dispatchUnhandledInputEvent(event);
+            }
         }
     };
 
@@ -347,7 +351,9 @@
         public void onSessionCreated(Session session) {
             if (this != mSessionCallback) {
                 // This callback is obsolete.
-                session.release();
+                if (session != null) {
+                    session.release();
+                }
                 return;
             }
             mSession = session;
diff --git a/core/java/android/util/Range.java b/core/java/android/util/Range.java
index 9a4bd4b..d7e8cf0 100644
--- a/core/java/android/util/Range.java
+++ b/core/java/android/util/Range.java
@@ -18,7 +18,7 @@
 
 import static com.android.internal.util.Preconditions.*;
 
-import android.hardware.camera2.impl.HashCodeHelpers;
+import android.hardware.camera2.utils.HashCodeHelpers;
 
 /**
  * Immutable class for describing the range of two numeric values.
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 5653066..17035b1 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -19,7 +19,11 @@
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.Trace;
+import android.util.Log;
 import android.util.TimeUtils;
 import android.view.Surface.OutOfResourcesException;
 import android.view.View.AttachInfo;
@@ -65,6 +69,8 @@
     private Choreographer mChoreographer;
 
     ThreadedRenderer(boolean translucent) {
+        AtlasInitializer.sInstance.init();
+
         long rootNodePtr = nCreateRootRenderNode();
         mRootNode = RenderNode.adopt(rootNodePtr);
         mRootNode.setClipToBounds(false);
@@ -292,8 +298,43 @@
         }
     }
 
-    /** @hide */
-    public static native void postToRenderThread(Runnable runnable);
+    private static class AtlasInitializer {
+        static AtlasInitializer sInstance = new AtlasInitializer();
+
+        private boolean mInitialized = false;
+
+        private AtlasInitializer() {}
+
+        synchronized void init() {
+            if (mInitialized) return;
+            IBinder binder = ServiceManager.getService("assetatlas");
+            if (binder == null) return;
+
+            IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder);
+            try {
+                if (atlas.isCompatible(android.os.Process.myPpid())) {
+                    GraphicBuffer buffer = atlas.getBuffer();
+                    if (buffer != null) {
+                        long[] map = atlas.getMap();
+                        if (map != null) {
+                            nSetAtlas(buffer, map);
+                            mInitialized = true;
+                        }
+                        // If IAssetAtlas is not the same class as the IBinder
+                        // we are using a remote service and we can safely
+                        // destroy the graphic buffer
+                        if (atlas.getClass() != binder.getClass()) {
+                            buffer.destroy();
+                        }
+                    }
+                }
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, "Could not acquire atlas", e);
+            }
+        }
+    }
+
+    private static native void nSetAtlas(GraphicBuffer buffer, long[] map);
 
     private static native long nCreateRootRenderNode();
     private static native long nCreateProxy(boolean translucent, long rootRenderNode);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index bef96b1..e829141 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -670,7 +670,7 @@
  * @attr ref android.R.styleable#View_scrollbarAlwaysDrawHorizontalTrack
  * @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack
  * @attr ref android.R.styleable#View_stateListAnimator
- * @attr ref android.R.styleable#View_sharedElementName
+ * @attr ref android.R.styleable#View_viewName
  * @attr ref android.R.styleable#View_soundEffectsEnabled
  * @attr ref android.R.styleable#View_tag
  * @attr ref android.R.styleable#View_textAlignment
@@ -3185,6 +3185,8 @@
     private int mBackgroundResource;
     private boolean mBackgroundSizeChanged;
 
+    private String mViewName;
+
     static class ListenerInfo {
         /**
          * Listener used to dispatch focus change events.
@@ -3997,8 +3999,8 @@
                 case R.styleable.View_accessibilityLiveRegion:
                     setAccessibilityLiveRegion(a.getInt(attr, ACCESSIBILITY_LIVE_REGION_DEFAULT));
                     break;
-                case R.styleable.View_sharedElementName:
-                    setSharedElementName(a.getString(attr));
+                case R.styleable.View_viewName:
+                    setViewName(a.getString(attr));
                     break;
                 case R.styleable.View_nestedScrollingEnabled:
                     setNestedScrollingEnabled(a.getBoolean(attr, false));
@@ -18839,15 +18841,15 @@
     }
 
     /**
-     * Adds all Views that have {@link #getSharedElementName()} non-null to sharedElements.
-     * @param sharedElements Will contain all Views in the hierarchy having a shared element name.
+     * Adds all Views that have {@link #getViewName()} non-null to namedElements.
+     * @param namedElements Will contain all Views in the hierarchy having a view name.
      * @hide
      */
-    public void findSharedElements(Map<String, View> sharedElements) {
+    public void findNamedViews(Map<String, View> namedElements) {
         if (getVisibility() == VISIBLE) {
-            String sharedElementName = getSharedElementName();
-            if (sharedElementName != null) {
-                sharedElements.put(sharedElementName, this);
+            String viewName = getViewName();
+            if (viewName != null) {
+                namedElements.put(viewName, this);
             }
         }
     }
@@ -19247,32 +19249,26 @@
     }
 
     /**
-     * Specifies that the shared name of the View to be shared with another Activity.
-     * When transitioning between Activities, the name links a UI element in the starting
-     * Activity to UI element in the called Activity. Names should be unique in the
-     * View hierarchy.
+     * Sets the name of the View to be used to identify Views in Transitions.
+     * Names should be unique in the View hierarchy.
      *
-     * @param sharedElementName The cross-Activity View identifier. The called Activity will use
-     *                 the name to match the location with a View in its layout.
-     * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.os.Bundle)
+     * @param viewName The name of the View to uniquely identify it for Transitions.
      */
-    public void setSharedElementName(String sharedElementName) {
-        setTagInternal(com.android.internal.R.id.shared_element_name, sharedElementName);
+    public final void setViewName(String viewName) {
+        mViewName = viewName;
     }
 
     /**
-     * Returns the shared name of the View to be shared with another Activity.
-     * When transitioning between Activities, the name links a UI element in the starting
-     * Activity to UI element in the called Activity. Names should be unique in the
-     * View hierarchy.
+     * Returns the name of the View to be used to identify Views in Transitions.
+     * Names should be unique in the View hierarchy.
      *
-     * <p>This returns null if the View is not a shared element or the name if it is.</p>
+     * <p>This returns null if the View has not been given a name.</p>
      *
-     * @return The name used for this View for cross-Activity transitions or null if
-     * this View has not been identified as shared.
+     * @return The name used of the View to be used to identify Views in Transitions or null
+     * if no name has been given.
      */
-    public String getSharedElementName() {
-        return (String) getTag(com.android.internal.R.id.shared_element_name);
+    public String getViewName() {
+        return mViewName;
     }
 
     /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 4309366..92a42b7 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2301,13 +2301,13 @@
      * individually during the transition.
      * @return True if the ViewGroup should be acted on together during an Activity transition.
      * The default value is false when the background is null and true when the background
-     * is not null or if {@link #getSharedElementName()} is not null.
+     * is not null or if {@link #getViewName()} is not null.
      */
     public boolean isTransitionGroup() {
         if ((mGroupFlags & FLAG_IS_TRANSITION_GROUP_SET) != 0) {
             return ((mGroupFlags & FLAG_IS_TRANSITION_GROUP) != 0);
         } else {
-            return getBackground() != null || getSharedElementName() != null;
+            return getBackground() != null || getViewName() != null;
         }
     }
 
@@ -5956,15 +5956,15 @@
 
     /** @hide */
     @Override
-    public void findSharedElements(Map<String, View> sharedElements) {
+    public void findNamedViews(Map<String, View> namedElements) {
         if (getVisibility() != VISIBLE) {
             return;
         }
-        super.findSharedElements(sharedElements);
+        super.findNamedViews(namedElements);
         int count = getChildCount();
         for (int i = 0; i < count; i++) {
             View child = getChildAt(i);
-            child.findSharedElements(sharedElements);
+            child.findNamedViews(namedElements);
         }
     }
 
diff --git a/core/java/android/view/animation/AccelerateInterpolator.java b/core/java/android/view/animation/AccelerateInterpolator.java
index dcab743..c08f348 100644
--- a/core/java/android/view/animation/AccelerateInterpolator.java
+++ b/core/java/android/view/animation/AccelerateInterpolator.java
@@ -20,12 +20,17 @@
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
 
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
 /**
  * An interpolator where the rate of change starts out slowly and 
  * and then accelerates.
  *
  */
-public class AccelerateInterpolator implements Interpolator {
+@HasNativeInterpolator
+public class AccelerateInterpolator implements Interpolator, NativeInterpolatorFactory {
     private final float mFactor;
     private final double mDoubleFactor;
 
@@ -64,4 +69,10 @@
             return (float)Math.pow(input, mDoubleFactor);
         }
     }
+
+    /** @hide */
+    @Override
+    public long createNativeInterpolator() {
+        return NativeInterpolatorFactoryHelper.createAccelerateInterpolator(mFactor);
+    }
 }
diff --git a/core/java/android/view/animation/AnticipateInterpolator.java b/core/java/android/view/animation/AnticipateInterpolator.java
index a6f110e..83a8007 100644
--- a/core/java/android/view/animation/AnticipateInterpolator.java
+++ b/core/java/android/view/animation/AnticipateInterpolator.java
@@ -20,10 +20,15 @@
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
 
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
 /**
  * An interpolator where the change starts backward then flings forward.
  */
-public class AnticipateInterpolator implements Interpolator {
+@HasNativeInterpolator
+public class AnticipateInterpolator implements Interpolator, NativeInterpolatorFactory {
     private final float mTension;
 
     public AnticipateInterpolator() {
@@ -53,4 +58,10 @@
         // a(t) = t * t * ((tension + 1) * t - tension)
         return t * t * ((mTension + 1) * t - mTension);
     }
+
+    /** @hide */
+    @Override
+    public long createNativeInterpolator() {
+        return NativeInterpolatorFactoryHelper.createAnticipateInterpolator(mTension);
+    }
 }
diff --git a/core/java/android/view/animation/AnticipateOvershootInterpolator.java b/core/java/android/view/animation/AnticipateOvershootInterpolator.java
index 3dc9722..1a8adfd 100644
--- a/core/java/android/view/animation/AnticipateOvershootInterpolator.java
+++ b/core/java/android/view/animation/AnticipateOvershootInterpolator.java
@@ -19,6 +19,11 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
+
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
 import static com.android.internal.R.styleable.AnticipateOvershootInterpolator_extraTension;
 import static com.android.internal.R.styleable.AnticipateOvershootInterpolator_tension;
 import static com.android.internal.R.styleable.AnticipateOvershootInterpolator;
@@ -27,7 +32,8 @@
  * An interpolator where the change starts backward then flings forward and overshoots
  * the target value and finally goes back to the final value.
  */
-public class AnticipateOvershootInterpolator implements Interpolator {
+@HasNativeInterpolator
+public class AnticipateOvershootInterpolator implements Interpolator, NativeInterpolatorFactory {
     private final float mTension;
 
     public AnticipateOvershootInterpolator() {
@@ -80,4 +86,10 @@
         if (t < 0.5f) return 0.5f * a(t * 2.0f, mTension);
         else return 0.5f * (o(t * 2.0f - 2.0f, mTension) + 2.0f);
     }
+
+    /** @hide */
+    @Override
+    public long createNativeInterpolator() {
+        return NativeInterpolatorFactoryHelper.createAnticipateOvershootInterpolator(mTension);
+    }
 }
diff --git a/core/java/android/view/animation/BounceInterpolator.java b/core/java/android/view/animation/BounceInterpolator.java
index ecf99a7..9d8ca90 100644
--- a/core/java/android/view/animation/BounceInterpolator.java
+++ b/core/java/android/view/animation/BounceInterpolator.java
@@ -19,10 +19,15 @@
 import android.content.Context;
 import android.util.AttributeSet;
 
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
 /**
  * An interpolator where the change bounces at the end.
  */
-public class BounceInterpolator implements Interpolator {
+@HasNativeInterpolator
+public class BounceInterpolator implements Interpolator, NativeInterpolatorFactory {
     public BounceInterpolator() {
     }
 
@@ -47,4 +52,10 @@
         else if (t < 0.9644f) return bounce(t - 0.8526f) + 0.9f;
         else return bounce(t - 1.0435f) + 0.95f;
     }
+
+    /** @hide */
+    @Override
+    public long createNativeInterpolator() {
+        return NativeInterpolatorFactoryHelper.createBounceInterpolator();
+    }
 }
\ No newline at end of file
diff --git a/core/java/android/view/animation/CycleInterpolator.java b/core/java/android/view/animation/CycleInterpolator.java
index d355c23..d1ebf05 100644
--- a/core/java/android/view/animation/CycleInterpolator.java
+++ b/core/java/android/view/animation/CycleInterpolator.java
@@ -20,12 +20,17 @@
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
 
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
 /**
  * Repeats the animation for a specified number of cycles. The
  * rate of change follows a sinusoidal pattern.
  *
  */
-public class CycleInterpolator implements Interpolator {
+@HasNativeInterpolator
+public class CycleInterpolator implements Interpolator, NativeInterpolatorFactory {
     public CycleInterpolator(float cycles) {
         mCycles = cycles;
     }
@@ -44,4 +49,10 @@
     }
     
     private float mCycles;
+
+    /** @hide */
+    @Override
+    public long createNativeInterpolator() {
+        return NativeInterpolatorFactoryHelper.createCycleInterpolator(mCycles);
+    }
 }
diff --git a/core/java/android/view/animation/DecelerateInterpolator.java b/core/java/android/view/animation/DecelerateInterpolator.java
index 20e079b..0789a0e 100644
--- a/core/java/android/view/animation/DecelerateInterpolator.java
+++ b/core/java/android/view/animation/DecelerateInterpolator.java
@@ -20,12 +20,17 @@
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
 
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
 /**
  * An interpolator where the rate of change starts out quickly and 
  * and then decelerates.
  *
  */
-public class DecelerateInterpolator implements Interpolator {
+@HasNativeInterpolator
+public class DecelerateInterpolator implements Interpolator, NativeInterpolatorFactory {
     public DecelerateInterpolator() {
     }
 
@@ -60,4 +65,10 @@
     }
     
     private float mFactor = 1.0f;
+
+    /** @hide */
+    @Override
+    public long createNativeInterpolator() {
+        return NativeInterpolatorFactoryHelper.createDecelerateInterpolator(mFactor);
+    }
 }
diff --git a/core/java/android/view/animation/LinearInterpolator.java b/core/java/android/view/animation/LinearInterpolator.java
index 96a039f..552c611 100644
--- a/core/java/android/view/animation/LinearInterpolator.java
+++ b/core/java/android/view/animation/LinearInterpolator.java
@@ -19,11 +19,16 @@
 import android.content.Context;
 import android.util.AttributeSet;
 
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
 /**
  * An interpolator where the rate of change is constant
  *
  */
-public class LinearInterpolator implements Interpolator {
+@HasNativeInterpolator
+public class LinearInterpolator implements Interpolator, NativeInterpolatorFactory {
 
     public LinearInterpolator() {
     }
@@ -34,4 +39,10 @@
     public float getInterpolation(float input) {
         return input;
     }
+
+    /** @hide */
+    @Override
+    public long createNativeInterpolator() {
+        return NativeInterpolatorFactoryHelper.createLinearInterpolator();
+    }
 }
diff --git a/core/java/android/view/animation/OvershootInterpolator.java b/core/java/android/view/animation/OvershootInterpolator.java
index 494f8ab..a2466f1 100644
--- a/core/java/android/view/animation/OvershootInterpolator.java
+++ b/core/java/android/view/animation/OvershootInterpolator.java
@@ -20,11 +20,16 @@
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
 
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
 /**
  * An interpolator where the change flings forward and overshoots the last value
  * then comes back.
  */
-public class OvershootInterpolator implements Interpolator {
+@HasNativeInterpolator
+public class OvershootInterpolator implements Interpolator, NativeInterpolatorFactory {
     private final float mTension;
 
     public OvershootInterpolator() {
@@ -56,4 +61,10 @@
         t -= 1.0f;
         return t * t * ((mTension + 1) * t + mTension) + 1.0f;
     }
+
+    /** @hide */
+    @Override
+    public long createNativeInterpolator() {
+        return NativeInterpolatorFactoryHelper.createOvershootInterpolator(mTension);
+    }
 }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index be0c27d..f874eb7 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -56,6 +56,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -317,10 +318,17 @@
     int mCursorSelEnd;
     int mCursorCandStart;
     int mCursorCandEnd;
+
+    /**
+     * The instance that has previously been sent to the input method.
+     */
+    private CursorAnchorInfo mCursorAnchorInfo = null;
+
     /**
      * The buffer to retrieve the view location in screen coordinates in {@link #updateCursor}.
      */
     private final int[] mViewTopLeft = new int[2];
+
     // -----------------------------------------------------------
     
     /**
@@ -489,6 +497,9 @@
                 case SET_CURSOR_ANCHOR_MONITOR_MODE: {
                     synchronized (mH) {
                         mCursorAnchorMonitorMode = msg.arg1;
+                        // Clear the cache.
+                        mCursorRect.setEmpty();
+                        mCursorAnchorInfo = null;
                     }
                     return;
                 }
@@ -1173,6 +1184,7 @@
                 mCursorCandStart = -1;
                 mCursorCandEnd = -1;
                 mCursorRect.setEmpty();
+                mCursorAnchorInfo = null;
                 servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic, this);
             } else {
                 servedContext = null;
@@ -1535,10 +1547,9 @@
                     || mCurrentTextBoxAttribute == null || mCurMethod == null) {
                 return;
             }
+            if (DEBUG) Log.d(TAG, "updateCursor");
             mTmpCursorRect.set(left, top, right, bottom);
-            if (!mCursorRect.equals(mTmpCursorRect)) {
-                if (DEBUG) Log.d(TAG, "updateCursor");
-
+            if (!Objects.equals(mCursorRect, mTmpCursorRect)) {
                 try {
                     if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod);
                     mCursorRect.set(mTmpCursorRect);
@@ -1569,10 +1580,14 @@
                     || mCurrentTextBoxAttribute == null || mCurMethod == null) {
                 return;
             }
-            if (DEBUG) Log.d(TAG, "updateCursorAnchorInfo");
-
+            if (Objects.equals(mCursorAnchorInfo, cursorAnchorInfo)) {
+                Log.w(TAG, "Ignoring redundant updateCursorAnchorInfo: info=" + cursorAnchorInfo);
+                return;
+            }
+            if (DEBUG) Log.v(TAG, "updateCursorAnchorInfo: " + cursorAnchorInfo);
             try {
                 mCurMethod.updateCursorAnchorInfo(cursorAnchorInfo);
+                mCursorAnchorInfo = cursorAnchorInfo;
             } catch (RemoteException e) {
                 Log.w(TAG, "IME died: " + mCurId, e);
             }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index d9a4f57..f91ef1a 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3404,7 +3404,7 @@
                                     mTouchMode = TOUCH_MODE_OVERSCROLL;
                                 }
                                 if (incrementalDeltaY > 0) {
-                                    mEdgeGlowTop.onPull((float) overscroll / getHeight(),
+                                    mEdgeGlowTop.onPull((float) -overscroll / getHeight(),
                                             (float) x / getWidth());
                                     if (!mEdgeGlowBottom.isFinished()) {
                                         mEdgeGlowBottom.onRelease();
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 83fbe8f..c4a40b4 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -22,14 +22,10 @@
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.graphics.Xfermode;
-import android.util.Log;
-import com.android.internal.R;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
+import android.util.FloatMath;
 import android.view.animation.AnimationUtils;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
@@ -62,12 +58,9 @@
     // Time it will take before a pulled glow begins receding in ms
     private static final int PULL_TIME = 167;
 
-    // Time it will take in ms for a pulled glow to decay to partial strength before release
-    private static final int PULL_DECAY_TIME = 1000;
-
     private static final float MAX_ALPHA = 1.f;
 
-    private static final float MAX_GLOW_HEIGHT = 1.5f;
+    private static final float MAX_GLOW_SCALE = 2.f;
 
     private static final float PULL_GLOW_BEGIN = 0.f;
 
@@ -78,7 +71,9 @@
 
     private static final float EPSILON = 0.001f;
 
-    private static final float SIN_45 = (float) Math.sin(Math.PI / 4);
+    private static final double ANGLE = Math.PI / 6;
+    private static final float SIN = (float) Math.sin(ANGLE);
+    private static final float COS = (float) Math.cos(ANGLE);
 
     private float mGlowAlpha;
     private float mGlowScaleY;
@@ -114,6 +109,7 @@
     private final RectF mArcRect = new RectF();
     private final Paint mPaint = new Paint();
     private float mRadius;
+    private float mBaseGlowHeight;
     private float mDisplacement = 0.5f;
     private float mTargetDisplacement = 0.5f;
 
@@ -128,7 +124,7 @@
         final int themeColor = a.getColor(
                 com.android.internal.R.styleable.EdgeEffect_colorPrimaryLight, 0xff666666);
         a.recycle();
-        mPaint.setColor((themeColor & 0xffffff) | 0x66000000);
+        mPaint.setColor((themeColor & 0xffffff) | 0x33000000);
         mPaint.setStyle(Paint.Style.FILL);
         mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
         mInterpolator = new DecelerateInterpolator();
@@ -141,10 +137,11 @@
      * @param height Effect height in pixels
      */
     public void setSize(int width, int height) {
-        final float r = width * 0.5f / SIN_45;
-        final float y = SIN_45 * r;
+        final float r = width * 0.75f / SIN;
+        final float y = COS * r;
         final float h = r - y;
         mRadius = r;
+        mBaseGlowHeight = h;
 
         mBounds.set(mBounds.left, mBounds.top, width, (int) Math.min(height, h));
     }
@@ -214,21 +211,18 @@
 
         mPullDistance += deltaDistance;
 
+        final float absdd = Math.abs(deltaDistance);
         mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA,
-                mGlowAlpha +
-                        (Math.abs(deltaDistance) * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
+                mGlowAlpha + (absdd * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
 
-        float glowChange = Math.abs(deltaDistance);
-        if (deltaDistance > 0 && mPullDistance < 0) {
-            glowChange = -glowChange;
-        }
         if (mPullDistance == 0) {
-            mGlowScaleY = 0;
-        }
+            mGlowScaleY = mGlowScaleYStart = 0;
+        } else {
+            final float scale = Math.max(0, 1 - 1 /
+                    FloatMath.sqrt(Math.abs(mPullDistance) * mBounds.height()) - 0.3f) / 0.7f;
 
-        // Do not allow glow to get larger than MAX_GLOW_HEIGHT.
-        mGlowScaleY = mGlowScaleYStart = Math.min(MAX_GLOW_HEIGHT, Math.max(
-                0, mGlowScaleY + glowChange * PULL_DISTANCE_GLOW_FACTOR));
+            mGlowScaleY = mGlowScaleYStart = scale;
+        }
 
         mGlowAlphaFinish = mGlowAlpha;
         mGlowScaleYFinish = mGlowScaleY;
@@ -311,19 +305,17 @@
         final float y = mBounds.height();
         final float centerY = y - mRadius;
         final float centerX = mBounds.centerX();
+
         mArcRect.set(centerX - mRadius, centerY - mRadius, centerX + mRadius, centerY + mRadius);
         canvas.scale(1.f, Math.min(mGlowScaleY, 1.f), centerX, 0);
 
         final float displacement = Math.max(0, Math.min(mDisplacement, 1.f)) - 0.5f;
-        float translateX = mBounds.width() * displacement;
-        float translateY = 0;
-        if (mGlowScaleY > 1.f) {
-            translateY = (mGlowScaleY - 1.f) * mBounds.height();
-        }
+        float translateX = mBounds.width() * displacement / 2;
+
         canvas.clipRect(Float.MIN_VALUE, mBounds.top,
                 Float.MAX_VALUE, Float.MAX_VALUE);
-        canvas.translate(translateX, translateY);
-        canvas.drawArc(mArcRect, 0, 180, true, mPaint);
+        canvas.translate(translateX, 0);
+        canvas.drawArc(mArcRect, 45, 90, true, mPaint);
         canvas.restoreToCount(count);
 
         boolean oneLastFrame = false;
@@ -341,7 +333,7 @@
      * @return The maximum height of the edge effect
      */
     public int getMaxHeight() {
-        return (int) (mBounds.height() * MAX_GLOW_HEIGHT + 0.5f);
+        return (int) (mBounds.height() * MAX_GLOW_SCALE + 0.5f);
     }
 
     private void update() {
@@ -369,16 +361,7 @@
                     mGlowScaleYFinish = 0.f;
                     break;
                 case STATE_PULL:
-                    mState = STATE_PULL_DECAY;
-                    mStartTime = AnimationUtils.currentAnimationTimeMillis();
-                    mDuration = PULL_DECAY_TIME;
-
-                    mGlowAlphaStart = mGlowAlpha;
-                    mGlowScaleYStart = mGlowScaleY;
-
-                    // After pull, the glow should fade to nothing.
-                    mGlowAlphaFinish = 0.f;
-                    mGlowScaleYFinish = 0.f;
+                    // Hold in this state until explicitly released.
                     break;
                 case STATE_PULL_DECAY:
                     mState = STATE_RECEDE;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index b0a4e24..cbe7511 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -39,6 +39,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.Rect;
@@ -94,6 +95,8 @@
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
@@ -215,6 +218,8 @@
 
     private TextView mTextView;
 
+    final CursorAnchorInfoNotifier mCursorAnchorInfoNotifier = new CursorAnchorInfoNotifier();
+
     Editor(TextView textView) {
         mTextView = textView;
     }
@@ -249,9 +254,13 @@
             // We had an active selection from before, start the selection mode.
             startSelectionActionMode();
         }
+
+        getPositionListener().addSubscriber(mCursorAnchorInfoNotifier, true);
     }
 
     void onDetachedFromWindow() {
+        getPositionListener().removeSubscriber(mCursorAnchorInfoNotifier);
+
         if (mError != null) {
             hideError();
         }
@@ -780,7 +789,7 @@
                 boolean parentPositionChanged, boolean parentScrolled);
     }
 
-    private boolean isPositionVisible(int positionX, int positionY) {
+    private boolean isPositionVisible(final float positionX, final float positionY) {
         synchronized (TEMP_POSITION) {
             final float[] position = TEMP_POSITION;
             position[0] = positionX;
@@ -2134,7 +2143,8 @@
     private class PositionListener implements ViewTreeObserver.OnPreDrawListener {
         // 3 handles
         // 3 ActionPopup [replace, suggestion, easyedit] (suggestionsPopup first hides the others)
-        private final int MAXIMUM_NUMBER_OF_LISTENERS = 6;
+        // 1 CursorAnchorInfoNotifier
+        private final int MAXIMUM_NUMBER_OF_LISTENERS = 7;
         private TextViewPositionListener[] mPositionListeners =
                 new TextViewPositionListener[MAXIMUM_NUMBER_OF_LISTENERS];
         private boolean mCanMove[] = new boolean[MAXIMUM_NUMBER_OF_LISTENERS];
@@ -2997,6 +3007,116 @@
         }
     }
 
+    /**
+     * A listener to call {@link InputMethodManager#updateCursorAnchorInfo(View, CursorAnchorInfo)}
+     * while the input method is requesting the cursor/anchor position. Does nothing as long as
+     * {@link InputMethodManager#isWatchingCursor(View)} returns false.
+     */
+    private final class CursorAnchorInfoNotifier implements TextViewPositionListener {
+        final CursorAnchorInfoBuilder mSelectionInfoBuilder = new CursorAnchorInfoBuilder();
+        final int[] mTmpIntOffset = new int[2];
+        final Matrix mViewToScreenMatrix = new Matrix();
+
+        @Override
+        public void updatePosition(int parentPositionX, int parentPositionY,
+                boolean parentPositionChanged, boolean parentScrolled) {
+            final InputMethodState ims = mInputMethodState;
+            if (ims == null || ims.mBatchEditNesting > 0) {
+                return;
+            }
+            final InputMethodManager imm = InputMethodManager.peekInstance();
+            if (null == imm) {
+                return;
+            }
+            // Skip if the IME has not requested the cursor/anchor position.
+            if (!imm.isWatchingCursor(mTextView)) {
+                return;
+            }
+            Layout layout = mTextView.getLayout();
+            if (layout == null) {
+                return;
+            }
+
+            final CursorAnchorInfoBuilder builder = mSelectionInfoBuilder;
+            builder.reset();
+
+            final int selectionStart = mTextView.getSelectionStart();
+            final int selectionEnd = mTextView.getSelectionEnd();
+            builder.setSelectionRange(mTextView.getSelectionStart(), mTextView.getSelectionEnd());
+
+            // Construct transformation matrix from view local coordinates to screen coordinates.
+            mViewToScreenMatrix.set(mTextView.getMatrix());
+            mTextView.getLocationOnScreen(mTmpIntOffset);
+            mViewToScreenMatrix.postTranslate(mTmpIntOffset[0], mTmpIntOffset[1]);
+            builder.setMatrix(mViewToScreenMatrix);
+
+            final float viewportToContentHorizontalOffset =
+                    mTextView.viewportToContentHorizontalOffset();
+            final float viewportToContentVerticalOffset =
+                    mTextView.viewportToContentVerticalOffset();
+
+            if (mTextView.getText() instanceof Spannable) {
+                final Spannable sp = (Spannable) mTextView.getText();
+                int compositionStart = EditableInputConnection.getComposingSpanStart(sp);
+                int compositionEnd = EditableInputConnection.getComposingSpanEnd(sp);
+                if (compositionEnd < compositionStart) {
+                    final int temp = compositionEnd;
+                    compositionEnd = compositionStart;
+                    compositionStart = temp;
+                }
+                builder.setCandidateRange(compositionStart, compositionEnd);
+                for (int offset = compositionStart; offset < compositionEnd; offset++) {
+                    if (offset < 0) {
+                        continue;
+                    }
+                    final int line = layout.getLineForOffset(offset);
+                    final float left = layout.getPrimaryHorizontal(offset)
+                            + viewportToContentHorizontalOffset;
+                    final float top = layout.getLineTop(line) + viewportToContentVerticalOffset;
+                    // Here we are tentatively passing offset + 1 to calculate the other side of
+                    // the primary horizontal to preserve as many positions as possible so that
+                    // the IME can reconstruct the layout entirely. However, we should revisit this
+                    // to have a clear specification about the relationship between the index of
+                    // the character and its bounding box. See also the TODO comment below.
+                    final float right = layout.getPrimaryHorizontal(offset + 1)
+                            + viewportToContentHorizontalOffset;
+                    final float bottom = layout.getLineBottom(line)
+                            + viewportToContentVerticalOffset;
+                    // Take TextView's padding and scroll into account.
+                    if (isPositionVisible(left, top) && isPositionVisible(right, bottom)) {
+                        // Here offset is the index in Java chars.
+                        // TODO: We must have a well-defined specification. For example, how
+                        // RTL, surrogate pairs, and composition letters are handled must be
+                        // documented.
+                        builder.addCharacterRect(offset, left, top, right, bottom);
+                    }
+                }
+            }
+
+            // Treat selectionStart as the insertion point.
+            if (0 <= selectionStart) {
+                final int offset = selectionStart;
+                final int line = layout.getLineForOffset(offset);
+                final float insertionMarkerX = layout.getPrimaryHorizontal(offset)
+                        + viewportToContentHorizontalOffset;
+                final float insertionMarkerTop = layout.getLineTop(line)
+                        + viewportToContentVerticalOffset;
+                final float insertionMarkerBaseline = layout.getLineBaseline(line)
+                        + viewportToContentVerticalOffset;
+                final float insertionMarkerBottom = layout.getLineBottom(line)
+                        + viewportToContentVerticalOffset;
+                // Take TextView's padding and scroll into account.
+                if (isPositionVisible(insertionMarkerX, insertionMarkerTop) &&
+                        isPositionVisible(insertionMarkerX, insertionMarkerBottom)) {
+                    builder.setInsertionMarkerLocation(insertionMarkerX, insertionMarkerTop,
+                            insertionMarkerBaseline, insertionMarkerBottom);
+                }
+            }
+
+            imm.updateCursorAnchorInfo(mTextView, builder.build());
+        }
+    }
+
     private abstract class HandleView extends View implements TextViewPositionListener {
         protected Drawable mDrawable;
         protected Drawable mDrawableLtr;
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 2f74372..47ef65a 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -84,6 +84,7 @@
             Slog.e(TAG, "PackageManagerService is dead?");
         }
         if (canForward) {
+            newIntent.prepareToLeaveUser(callingUserId);
             startActivityAsUser(newIntent, userDest);
         } else {
             Slog.wtf(TAG, "the intent: " + newIntent + "cannot be forwarded from user "
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index bc92c4a..0937ec3 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -55,5 +55,6 @@
     public static final int BASE_DNS_PINGER                                         = 0x00050000;
     public static final int BASE_NSD_MANAGER                                        = 0x00060000;
     public static final int BASE_NETWORK_STATE_TRACKER                              = 0x00070000;
+    public static final int BASE_CONNECTIVITY_SERVICE                               = 0x00080000;
     //TODO: define all used protocols
 }
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index b35de93..5b59599 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -912,6 +912,15 @@
         }
     }
 
+    public static int readIntAttribute(XmlPullParser in, String name, int defaultValue) {
+        final String value = in.getAttributeValue(null, name);
+        try {
+            return Integer.parseInt(value);
+        } catch (NumberFormatException e) {
+            return defaultValue;
+        }
+    }
+
     public static int readIntAttribute(XmlPullParser in, String name) throws IOException {
         final String value = in.getAttributeValue(null, name);
         try {
diff --git a/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java b/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java
index 2b25280..7cd75f3 100644
--- a/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java
+++ b/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java
@@ -24,5 +24,13 @@
     private NativeInterpolatorFactoryHelper() {}
 
     public static native long createAccelerateDecelerateInterpolator();
+    public static native long createAccelerateInterpolator(float factor);
+    public static native long createAnticipateInterpolator(float tension);
+    public static native long createAnticipateOvershootInterpolator(float tension);
+    public static native long createBounceInterpolator();
+    public static native long createCycleInterpolator(float cycles);
+    public static native long createDecelerateInterpolator(float factor);
+    public static native long createLinearInterpolator();
+    public static native long createOvershootInterpolator(float tension);
     public static native long createLutInterpolator(float[] values);
 }
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 05a99a3..fa2cfe3 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -489,8 +489,13 @@
 
     sp<VendorTagDescriptor> vTags = VendorTagDescriptor::getGlobalVendorTagDescriptor();
 
-    SortedVector<String8> vendorSections = vTags->getAllSectionNames();
-    size_t vendorSectionCount = vendorSections.size();
+    SortedVector<String8> vendorSections;
+    size_t vendorSectionCount = 0;
+
+    if (vTags != 0) {
+        vendorSections = vTags->getAllSectionNames();
+        vendorSectionCount = vendorSections.size();
+    }
 
     // First, find the section by the longest string match
     const char *section = NULL;
@@ -561,7 +566,7 @@
                                  "Could not find tag name for key '%s')", key);
             return 0;
         }
-    } else {
+    } else if (vTags != 0) {
         // Match vendor tags (typically com.*)
         const String8 sectionName(section);
         const String8 tagName(keyTagName);
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index cdd036e..d130a6d 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -26,8 +26,11 @@
 #include <android_runtime/android_view_Surface.h>
 #include <system/window.h>
 
+#include "android_view_GraphicBuffer.h"
+
 #include <Animator.h>
 #include <RenderNode.h>
+#include <renderthread/CanvasContext.h>
 #include <renderthread/RenderProxy.h>
 #include <renderthread/RenderTask.h>
 #include <renderthread/RenderThread.h>
@@ -67,6 +70,26 @@
     jobject mRunnable;
 };
 
+class SetAtlasTask : public RenderTask {
+public:
+    SetAtlasTask(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size)
+            : mBuffer(buffer)
+            , mMap(map)
+            , mMapSize(size) {
+    }
+
+    virtual void run() {
+        CanvasContext::setTextureAtlas(mBuffer, mMap, mMapSize);
+        mMap = 0;
+        delete this;
+    }
+
+private:
+    sp<GraphicBuffer> mBuffer;
+    int64_t* mMap;
+    size_t mMapSize;
+};
+
 class OnFinishedEvent {
 public:
     OnFinishedEvent(BaseAnimator* animator, AnimationListener* listener)
@@ -127,9 +150,18 @@
     std::vector<OnFinishedEvent> mOnFinishedEvents;
 };
 
-static void android_view_ThreadedRenderer_postToRenderThread(JNIEnv* env, jobject clazz,
-        jobject jrunnable) {
-    RenderTask* task = new JavaTask(env, jrunnable);
+static void android_view_ThreadedRenderer_setAtlas(JNIEnv* env, jobject clazz,
+        jobject graphicBuffer, jlongArray atlasMapArray) {
+    sp<GraphicBuffer> buffer = graphicBufferForJavaObject(env, graphicBuffer);
+    jsize len = env->GetArrayLength(atlasMapArray);
+    if (len <= 0) {
+        ALOGW("Failed to initialize atlas, invalid map length: %d", len);
+        return;
+    }
+    int64_t* map = new int64_t[len];
+    env->GetLongArrayRegion(atlasMapArray, 0, len, map);
+
+    SetAtlasTask* task = new SetAtlasTask(buffer, map, len);
     RenderThread::getInstance().queue(task);
 }
 
@@ -275,7 +307,7 @@
 
 static JNINativeMethod gMethods[] = {
 #ifdef USE_OPENGL_RENDERER
-    { "postToRenderThread", "(Ljava/lang/Runnable;)V",   (void*) android_view_ThreadedRenderer_postToRenderThread },
+    { "nSetAtlas", "(Landroid/view/GraphicBuffer;[J)V",   (void*) android_view_ThreadedRenderer_setAtlas },
     { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },
     { "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy },
     { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy },
diff --git a/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp b/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
index c2d3dd1..704e1be 100644
--- a/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
+++ b/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
@@ -32,6 +32,38 @@
     return reinterpret_cast<jlong>(new AccelerateDecelerateInterpolator());
 }
 
+static jlong createAccelerateInterpolator(JNIEnv* env, jobject clazz, jfloat factor) {
+    return reinterpret_cast<jlong>(new AccelerateInterpolator(factor));
+}
+
+static jlong createAnticipateInterpolator(JNIEnv* env, jobject clazz, jfloat tension) {
+    return reinterpret_cast<jlong>(new AnticipateInterpolator(tension));
+}
+
+static jlong createAnticipateOvershootInterpolator(JNIEnv* env, jobject clazz, jfloat tension) {
+    return reinterpret_cast<jlong>(new AnticipateOvershootInterpolator(tension));
+}
+
+static jlong createBounceInterpolator(JNIEnv* env, jobject clazz) {
+    return reinterpret_cast<jlong>(new BounceInterpolator());
+}
+
+static jlong createCycleInterpolator(JNIEnv* env, jobject clazz, jfloat cycles) {
+    return reinterpret_cast<jlong>(new CycleInterpolator(cycles));
+}
+
+static jlong createDecelerateInterpolator(JNIEnv* env, jobject clazz, jfloat factor) {
+    return reinterpret_cast<jlong>(new DecelerateInterpolator(factor));
+}
+
+static jlong createLinearInterpolator(JNIEnv* env, jobject clazz) {
+    return reinterpret_cast<jlong>(new LinearInterpolator());
+}
+
+static jlong createOvershootInterpolator(JNIEnv* env, jobject clazz, jfloat tension) {
+    return reinterpret_cast<jlong>(new OvershootInterpolator(tension));
+}
+
 static jlong createLutInterpolator(JNIEnv* env, jobject clazz, jfloatArray jlut) {
     jsize len = env->GetArrayLength(jlut);
     if (len <= 0) {
@@ -53,6 +85,14 @@
 static JNINativeMethod gMethods[] = {
 #ifdef USE_OPENGL_RENDERER
     { "createAccelerateDecelerateInterpolator", "()J", (void*) createAccelerateDecelerateInterpolator },
+    { "createAccelerateInterpolator", "(F)J", (void*) createAccelerateInterpolator },
+    { "createAnticipateInterpolator", "(F)J", (void*) createAnticipateInterpolator },
+    { "createAnticipateOvershootInterpolator", "(F)J", (void*) createAnticipateOvershootInterpolator },
+    { "createBounceInterpolator", "()J", (void*) createBounceInterpolator },
+    { "createCycleInterpolator", "(F)J", (void*) createCycleInterpolator },
+    { "createDecelerateInterpolator", "(F)J", (void*) createDecelerateInterpolator },
+    { "createLinearInterpolator", "()J", (void*) createLinearInterpolator },
+    { "createOvershootInterpolator", "(F)J", (void*) createOvershootInterpolator },
     { "createLutInterpolator", "([F)J", (void*) createLutInterpolator },
 #endif
 };
diff --git a/core/res/res/drawable/btn_color_quantum.xml b/core/res/res/drawable/btn_color_quantum.xml
deleted file mode 100644
index 2da9226..0000000
--- a/core/res/res/drawable/btn_color_quantum.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
--->
-
-<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
-    android:tint="?attr/colorButtonPressedColored">
-    <selector>
-        <item android:state_enabled="false">
-            <nine-patch android:src="@drawable/btn_qntm_alpha"
-                android:tint="?attr/colorButtonNormal"
-                android:alpha="?attr/disabledAlpha" />
-        </item>
-        <item>
-            <nine-patch android:src="@drawable/btn_qntm_alpha"
-                android:tint="?attr/colorButtonNormalColored" />
-        </item>
-    </selector>
-</touch-feedback>
diff --git a/core/res/res/layout-xlarge/screen_action_bar.xml b/core/res/res/layout-xlarge/screen_action_bar.xml
index d2fe9fa..02c99fe 100644
--- a/core/res/res/layout-xlarge/screen_action_bar.xml
+++ b/core/res/res/layout-xlarge/screen_action_bar.xml
@@ -34,7 +34,7 @@
         android:layout_height="wrap_content"
         android:layout_alignParentTop="true"
         style="?android:attr/actionBarStyle"
-        android:sharedElementName="android:action_bar"
+        android:viewName="android:action_bar"
         android:gravity="top">
         <com.android.internal.widget.ActionBarView
             android:id="@+id/action_bar"
diff --git a/core/res/res/layout/screen_action_bar.xml b/core/res/res/layout/screen_action_bar.xml
index 77f537b..eb237b3 100644
--- a/core/res/res/layout/screen_action_bar.xml
+++ b/core/res/res/layout/screen_action_bar.xml
@@ -34,7 +34,7 @@
         android:layout_height="wrap_content"
         android:layout_alignParentTop="true"
         style="?attr/actionBarStyle"
-        android:sharedElementName="android:action_bar"
+        android:viewName="android:action_bar"
         android:gravity="top">
         <com.android.internal.widget.ActionBarView
             android:id="@+id/action_bar"
diff --git a/core/res/res/layout/screen_custom_title.xml b/core/res/res/layout/screen_custom_title.xml
index d02cc8b..c8952bf 100644
--- a/core/res/res/layout/screen_custom_title.xml
+++ b/core/res/res/layout/screen_custom_title.xml
@@ -31,7 +31,7 @@
     <FrameLayout android:id="@android:id/title_container" 
         android:layout_width="match_parent" 
         android:layout_height="?android:attr/windowTitleSize"
-        android:sharedElementName="android:title"
+        android:viewName="android:title"
         style="?android:attr/windowTitleBackgroundStyle">
     </FrameLayout>
     <FrameLayout android:id="@android:id/content"
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 4d6f0a3..bee6c21 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -68,7 +68,7 @@
   </plurals>
     <string name="imei" msgid="2625429890869005782">"IMEI"</string>
     <string name="meid" msgid="4841221237681254195">"MEID"</string>
-    <string name="ClipMmi" msgid="6952821216480289285">"លេខ​សម្គាល់​អ្នក​ហៅ​​ចូល"</string>
+    <string name="ClipMmi" msgid="6952821216480289285">"លេខ​សម្គាល់​អ្នក​ហៅ​​ចូល​"</string>
     <string name="ClirMmi" msgid="7784673673446833091">"លេខ​សម្គាល់​អ្នក​ហៅ​ចេញ"</string>
     <string name="CfMmi" msgid="5123218989141573515">"បញ្ជូន​ការ​ហៅ​បន្ត"</string>
     <string name="CwMmi" msgid="9129678056795016867">"រង់ចាំ​ការ​ហៅ"</string>
@@ -125,7 +125,7 @@
     <string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> ៖ មិន​បាន​បញ្ជូន​បន្ត"</string>
     <string name="fcComplete" msgid="3118848230966886575">"កូដ​លក្ខណៈ​ពេញលេញ។"</string>
     <string name="fcError" msgid="3327560126588500777">"បញ្ហា​ការ​តភ្ជាប់​ ឬ​កូដ​លក្ខណៈ​​​មិន​ត្រឹមត្រូវ​។"</string>
-    <string name="httpErrorOk" msgid="1191919378083472204">"យល់​ព្រម"</string>
+    <string name="httpErrorOk" msgid="1191919378083472204">"យល់​ព្រម​"</string>
     <string name="httpError" msgid="7956392511146698522">"មាន​កំហុស​បណ្ដាញ។"</string>
     <string name="httpErrorLookup" msgid="4711687456111963163">"រក​មិន​ឃើញ URL ។"</string>
     <string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"គ្រោងការណ៍​ផ្ទៀងផ្ទាត់​តំបន់បណ្ដាញ​មិន​ត្រូវ​បាន​គាំទ្រ។"</string>
@@ -183,7 +183,7 @@
     <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"បើក​សំឡេង"</string>
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ពេល​ជិះ​យន្តហោះ"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"បាន​បើក​របៀប​ពេល​ជិះ​យន្ត​ហោះ"</string>
-    <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បាន​បិទ​របៀបពេលជិះ​យន្តហោះ"</string>
+    <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បាន​បិទ​របៀបពេលជិះ​យន្តហោះ​"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"ការ​កំណត់"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
     <string name="safeMode" msgid="2788228061547930246">"របៀប​​​សុវត្ថិភាព"</string>
@@ -195,7 +195,7 @@
     <string name="permgrouplab_messages" msgid="7521249148445456662">"សារ​របស់​អ្នក"</string>
     <string name="permgroupdesc_messages" msgid="7821999071003699236">"អាន និង​សរសេរ​សារ SMS, អ៊ីមែល និង​សារ​ផ្សេងៗ​ទៀត​របស់​អ្នក។"</string>
     <string name="permgrouplab_personalInfo" msgid="3519163141070533474">"ព័ត៌មាន​ផ្ទាល់ខ្លួន​របស់​អ្នក"</string>
-    <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូល​ដំណើរការ​ព័ត៌មាន​ដោយ​ផ្ទាល់​អំពី​អ្នក​ ដែល​បា​ន​រក្សាទុក​ក្នុង​កាត​ទំនាក់ទំនង​របស់​អ្នក។"</string>
+    <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូល​ដំណើរការ​ព័ត៌មាន​ដោយ​ផ្ទាល់​អំពី​អ្នក​ ដែល​បា​ន​រក្សាទុក​ក្នុង​កាត​ទំនាក់ទំនង​របស់​អ្នក។​"</string>
     <string name="permgrouplab_socialInfo" msgid="5799096623412043791">"ព័ត៌មាន​សង្គម​របស់​អ្នក"</string>
     <string name="permgroupdesc_socialInfo" msgid="7129842457611643493">"ចូល​ដំណើរការ​ព័ត៌មាន​ដោយ​ផ្ទាល់​អំពី​ទំនាក់ទំនង និង​ការ​ភ្ជាប់​សង្គម​របស់​អ្នក។"</string>
     <string name="permgrouplab_location" msgid="635149742436692049">"ទីតាំង​របស់​អ្នក"</string>
@@ -386,7 +386,7 @@
     <string name="permdesc_readInputState" msgid="8387754901688728043">"ឲ្យ​កម្មវិធី​មើល​គ្រាប់​ចុច​ដែល​អ្នក​ចុច​ពេល​មាន​អន្តរកម្ម​ជា​មួយ​កម្មវិធី​ផ្សេង (ដូចជា បញ្ចូល​ពាក្យ​សម្ងាត់)។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindInputMethod" msgid="3360064620230515776">"ចង​ទៅ​វិធីសាស្ត្រ​បញ្ចូល"</string>
     <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"ឲ្យ​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​វិធី​សាស្ត្រ​បញ្ចូល។ មិន​គួរ​​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
-    <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចង​សេវា​កម្ម​ភាព​មធ្យោបាយ​ងាយស្រួល"</string>
+    <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចង​សេវា​កម្ម​ភាព​មធ្យោបាយ​ងាយស្រួល​"</string>
     <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"ឲ្យ​​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​សេវាកម្ម​ភាព​ងាយស្រួល។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindPrintService" msgid="8462815179572748761">"ចង​សេវាកម្ម​​បោះពុម្ព"</string>
     <string name="permdesc_bindPrintService" msgid="7960067623209111135">"ឲ្យ​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​សេវាកម្ម​ធាតុ​ក្រាហ្វិក។ មិន​គួរ​​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
@@ -404,7 +404,7 @@
     <string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"អនុញ្ញាត​ឲ្យ​ម្ចាស់​ភ្ជាប់​ទៅ​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​​របស់​សេវាកម្ម​អន្តរកម្ម​សំឡេង។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"ភ្ជាប់​ទៅ​ការ​បង្ហាញ​ពី​ចម្ងាយ"</string>
     <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"អនុញ្ញាត​ឲ្យ​ម្ចាស់​ភ្ជាប់​​ទៅ​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​ការ​បង្ហាញ​ពី​ចម្ងាយ។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
-    <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចង​សេវា​កម្ម​ធាតុ​ក្រាហ្វិក"</string>
+    <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចង​សេវា​កម្ម​ធាតុ​ក្រាហ្វិក​"</string>
     <string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"ឲ្យ​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​សេវាកម្ម​ធាតុ​ក្រាហ្វិក។ មិន​គួរ​​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"ភ្ជាប់​ទៅ​សេវាកម្ម​ក្រុមហ៊ុន​ផ្ដល់​ច្រក"</string>
     <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"អនុញ្ញាត​ឲ្យ​ម្ចាស់​ភ្ជាប់​ទៅ​ក្រុមហ៊ុន​ផ្ដល់​​ច្រក​ដែល​បាន​ចុះ​ឈ្មោះ។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
@@ -412,7 +412,7 @@
     <string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"ឲ្យ​ម្ចាស់​ផ្ញើ​គោលបំណង​​ទៅ​អ្នក​គ្រប់គ្រង​ឧបករណ៍។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindTvInput" msgid="5601264742478168987">"ភ្ជាប់​ទៅ​ការ​បញ្ចូល​ទូរទស្សន៍"</string>
     <string name="permdesc_bindTvInput" msgid="2371008331852001924">"អនុញ្ញាត​ឲ្យ​ម្ចាស់​ភ្ជាប់​ទៅ​ចំណុចប្រទាក់​កម្រិត​ខ្ពស់​នៃ​ការ​បញ្ចូល​ទូរទស្សន៍។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
-    <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ថែម​ ឬ​លុប​កម្មវិធី​គ្រប់គ្រង​​​ឧបករណ៍"</string>
+    <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ថែម​ ឬ​លុប​កម្មវិធី​គ្រប់គ្រង​​​ឧបករណ៍​​"</string>
     <string name="permdesc_manageDeviceAdmins" msgid="5025608167709942485">"អនុញ្ញាត​​​ឲ្យ​ម្ចាស់​​​បន្ថែម​ ឬ​លុប​កម្មវិធី​គ្រប់គ្រង​ឧបករណ៍​សកម្ម​ចេញ​។ មិន​គួរ​ប្រើ​សម្រាប់​កម្មវិធី​​ធម្មតា​ទេ​។"</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"ប្ដូរ​ទិស​អេក្រង់"</string>
     <string name="permdesc_setOrientation" msgid="3046126619316671476">"ឲ្យ​កម្មវិធី​ប្ដូរ​ការ​បង្វិល​អេក្រង់​នៅ​ពេល​ណា​មួយ។ មិន​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
@@ -424,9 +424,9 @@
     <string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"ឲ្យ​កម្មវិធី​ស្នើ​​សញ្ញា​ដែល​បាន​ផ្ដល់​ត្រូវ​ផ្ញើ​ទៅ​ដំណើរការ​ស្ថិតស្ថេរ​​ទាំង​អស់។"</string>
     <string name="permlab_persistentActivity" msgid="8841113627955563938">"ធ្វើ​ឲ្យ​កម្មវិធី​ដំណើរការ​ជា​និច្ច"</string>
     <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"ឲ្យ​កម្មវិធី​ធ្វើជា​ផ្នែក​​ស្ថិតស្ថេរ​ដោយ​ខ្លួន​ឯង​ក្នុង​អង្គ​ចងចាំ។ វា​អាច​កំណត់​អង្គ​ចងចាំ​ដែល​អាច​ប្រើ​បាន​ចំពោះ​កម្មវិធី​ផ្សេងៗ​ ដោយ​ធ្វើឲ្យ​កុំព្យូទ័រ​បន្ទះ​យឺត។"</string>
-    <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យ​កម្មវិធី ធ្វើជា​ផ្នែក​អចិន្ត្រៃយ៍​នៃ​ខ្លួន​ក្នុង​អង្គ​ចងចាំ។ វា​អាច​កម្រិត​អង្គ​ចងចាំ​អាច​ប្រើ​បាន​ ដើម្បី​ធ្វើ​ឲ្យ​កម្មវិធី​ផ្សេង​ធ្វើ​ឲ្យ​ទូរស័ព្ទ​របស់​អ្នក​យឺត។"</string>
+    <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យ​កម្មវិធី ធ្វើជា​ផ្នែក​អចិន្ត្រៃយ៍​នៃ​ខ្លួន​ក្នុង​អង្គ​ចងចាំ។ វា​អាច​កម្រិត​អង្គ​ចងចាំ​អាច​ប្រើ​បាន​ ដើម្បី​ធ្វើ​ឲ្យ​កម្មវិធី​ផ្សេង​ធ្វើ​ឲ្យ​ទូរស័ព្ទ​របស់​អ្នក​យឺត។​"</string>
     <string name="permlab_deletePackages" msgid="184385129537705938">"លុប​កម្មវិធី"</string>
-    <string name="permdesc_deletePackages" msgid="7411480275167205081">"ឲ្យ​កម្មវិធី​លុប​កញ្ចប់ Android ។ កម្មវិធី​ព្យាបាទ​អាច​ប្រើ​វា ដើម្បី​លុប​កម្មវិធី​សំខាន់​ៗ។"</string>
+    <string name="permdesc_deletePackages" msgid="7411480275167205081">"ឲ្យ​កម្មវិធី​លុប​កញ្ចប់ Android ។ កម្មវិធី​ព្យាបាទ​អាច​ប្រើ​វា ដើម្បី​លុប​កម្មវិធី​សំខាន់​ៗ។ ​"</string>
     <string name="permlab_clearAppUserData" msgid="274109191845842756">"លុប​ទិន្នន័យ​របស់​​កម្មវិធី​ផ្សេង"</string>
     <string name="permdesc_clearAppUserData" msgid="4625323684125459488">"ឲ្យ​កម្មវិធី​សម្អាត​ទិន្នន័យ​អ្នក​ប្រើ។"</string>
     <string name="permlab_deleteCacheFiles" msgid="3128665571837408675">"លុប​ឃ្លាំង​សម្ងាត់​កម្មវិធី​ផ្សេងៗ"</string>
@@ -477,7 +477,7 @@
     <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"ឲ្យ​កម្មវិធី​កែ​ទិន្នន័យ​អំពី​ទំនាក់ទំនង​របស់​អ្នក​ដែល​បាន​រក្សាទុក​ក្នុង​កុំព្យូទ័រ​បន្ទះ រួមមាន​ប្រេកង់​​ដែល​អ្នក​បាន​ហៅ អ៊ីមែល ឬ​ទាក់ទង​តាម​វិធី​ផ្សេងៗ​ជា​មួយ​ទំនាក់ទំនង​ជាក់លាក់។ សិទ្ធិ​​នេះ​អនុញ្ញាត​ឲ្យ​​​កម្មវិធី​លុប​ទិន្នន័យ​ទំនាក់ទំនង​របស់​អ្នក។"</string>
     <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"ឲ្យ​កម្មវិធី​កែ​ទិន្នន័យ​អំពី​ទំនាក់ទំនង​របស់​អ្នក​ដែល​បាន​រក្សាទុក​ក្នុង​ទូរស័ព្ទ​របស់​អ្នក រួមមាន​ប្រេកង់​ដែល​អ្នក​បាន​ហៅ អ៊ីមែល ឬ​បាន​ទាក់ទង​​តាម​វិធី​ផ្សេងៗ​ជា​មួយ​ទំនាក់​ទំនាក់​ជាក់លាក់។ សិទ្ធិ​នេះ​ឲ្យ​កម្មវិធី​លុប​ទិន្នន័យ​ទំនាក់ទំនង។"</string>
     <string name="permlab_readCallLog" msgid="3478133184624102739">"អាន​​កំណត់​ហេតុ​​​ហៅ"</string>
-    <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យ​កម្មវិធី​អាន​បញ្ជី​ហៅ​កុំព្យូទ័រ​បន្ទះ​របស់​អ្នក រួមមាន​ទិន្នន័យ​អំពី​ការ​ហៅ​ចូល និង​ចេញ។ សិទ្ធិ​នេះ​អនុញ្ញាត​ឲ្យ​កម្មវិធី​រក្សាទុក​ទិន្នន័យ​បញ្ជី​ហៅ​របស់​អ្នក ហើយ​កម្មវិធី​ព្យាបាទ​អាច​ចែករំលែក​ទិន្នន័យ​បញ្ជី​ហៅ​ដោយ​មិន​ឲ្យ​អ្នក​ដឹង។"</string>
+    <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យ​កម្មវិធី​អាន​បញ្ជី​ហៅ​កុំព្យូទ័រ​បន្ទះ​របស់​អ្នក រួមមាន​ទិន្នន័យ​អំពី​ការ​ហៅ​ចូល និង​ចេញ។ សិទ្ធិ​នេះ​អនុញ្ញាត​ឲ្យ​កម្មវិធី​រក្សាទុក​ទិន្នន័យ​បញ្ជី​ហៅ​របស់​អ្នក ហើយ​កម្មវិធី​ព្យាបាទ​អាច​ចែករំលែក​ទិន្នន័យ​បញ្ជី​ហៅ​ដោយ​មិន​ឲ្យ​អ្នក​ដឹង។​"</string>
     <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"ឲ្យ​កម្មវិធី​អាន​​​បញ្ជី​ហៅ​ទូរស័ព្ទ​របស់​អ្នក រួមមាន​ទិន្នន័យ​អំពី​ការ​ហៅ​ចូល និង​ចេញ។ សិទ្ធិ​នេះ​អនុញ្ញាត​ឲ្យ​កម្មវិធី​រក្សាទុក​ទិន្នន័យ​បញ្ជី​ហៅ​របស់​អ្នក ហើយ​កម្មវិធី​ព្យាបាទ​អាច​ចែករំលែក​ទិន្នន័យ​បញ្ជី​ហៅ​ដោយ​មិន​ឲ្យ​អ្នកដឹង។"</string>
     <string name="permlab_writeCallLog" msgid="8552045664743499354">"សរសេរ​បញ្ជី​ហៅ"</string>
     <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ឲ្យ​កម្មវិធី​កែ​បញ្ជី​ហៅ​កុំព្យូទ័រ​បន្ទះ​របស់​អ្នក​រួមមាន​ទិន្នន័យ​អំពី​ការ​ហៅ​ចូល និង​ចេញ។​កម្មវិធី​ព្យាបាទ​អាច​ប្រើ​វា ដើម្បី​លុប ឬ​កែ​បញ្ជី​ហៅ​របស់​អ្នក។"</string>
@@ -611,7 +611,7 @@
     <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"ឲ្យ​កម្មវិធី​កំណត់​ជំនួយ​ទំហំ​ផ្ទាំង​រូបភាព​ប្រព័ន្ធ។"</string>
     <string name="permlab_masterClear" msgid="2315750423139697397">"កំណត់​ប្រព័ន្ធ​ទៅ​លំនាំដើម​រោងចក្រ​ឡើងវិញ"</string>
     <string name="permdesc_masterClear" msgid="3665380492633910226">"ឲ្យ​កម្មវិធី​កំណត់​ប្រព័ន្ធ​​ដូច​ការ​កំណត់​ចេញ​ពី​រោងចក្រ​ឡើងវិញ​ពេញលេញ ដោយ​លុប​ទិន្នន័យ ការ​កំណត់​រចនាសម្ព័ន្ធ និង​កម្មវិធី​បាន​ដំឡើង។"</string>
-    <string name="permlab_setTime" msgid="2021614829591775646">"កំណត់​​ម៉ោង"</string>
+    <string name="permlab_setTime" msgid="2021614829591775646">"កំណត់​​ម៉ោង​"</string>
     <string name="permdesc_setTime" product="tablet" msgid="1896341438151152881">"ឲ្យ​កម្មវិធី​ប្ដូរ​ម៉ោង​កុំព្យូទ័រ​បន្ទះ។"</string>
     <string name="permdesc_setTime" product="default" msgid="1855702730738020">"ឲ្យ​កម្មវិធី​ប្ដូរ​ម៉ោង​ទូរស័ព្ទ។"</string>
     <string name="permlab_setTimeZone" msgid="2945079801013077340">"កំណត់​តំបន់​ពេលវេលា"</string>
@@ -777,7 +777,7 @@
   <string-array name="organizationTypes">
     <item msgid="7546335612189115615">"កន្លែង​ធ្វើការ"</item>
     <item msgid="4378074129049520373">"ផ្សេងៗ"</item>
-    <item msgid="3455047468583965104">"តាម​តម្រូវ​ការ"</item>
+    <item msgid="3455047468583965104">"តាម​តម្រូវ​ការ​"</item>
   </string-array>
   <string-array name="imProtocols">
     <item msgid="8595261363518459565">"AIM"</item>
@@ -793,7 +793,7 @@
     <string name="phoneTypeHome" msgid="2570923463033985887">"ផ្ទះ"</string>
     <string name="phoneTypeMobile" msgid="6501463557754751037">"​ចល័ត"</string>
     <string name="phoneTypeWork" msgid="8863939667059911633">"កន្លែង​ធ្វើការ"</string>
-    <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារ​កន្លែង​ធ្វើការ"</string>
+    <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារ​កន្លែង​ធ្វើការ​"</string>
     <string name="phoneTypeFaxHome" msgid="2067265972322971467">"ទូរសារ​ផ្ទះ"</string>
     <string name="phoneTypePager" msgid="7582359955394921732">"ភេយ័រ"</string>
     <string name="phoneTypeOther" msgid="1544425847868765990">"ផ្សេងៗ"</string>
@@ -918,7 +918,7 @@
     <string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"ព្យាយាម​លំនាំ​ច្រើន​ពេក"</string>
     <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"ដើម្បី​ដោះ​សោ ចូល​គណនី Google របស់​អ្នក។"</string>
     <string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"ឈ្មោះ​អ្នក​ប្រើ (អ៊ីមែល​)"</string>
-    <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាត់"</string>
+    <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាត់​"</string>
     <string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"ចូល"</string>
     <string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​មិន​ត្រឹមត្រូវ។"</string>
     <string name="lockscreen_glogin_account_recovery_hint" msgid="1696924763690379073">"ភ្លេច​ឈ្មោះ​អ្នក​ប្រើ ឬ​ពាក្យ​សម្ងាត់​របស់​អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
@@ -963,7 +963,7 @@
     <string name="factorytest_failed" msgid="5410270329114212041">"បាន​បរាជ័យ​ក្នុង​ការ​សាកល្បង​រោងចក្រ"</string>
     <string name="factorytest_not_system" msgid="4435201656767276723">"សកម្មភាព FACTORY_TEST ត្រូវ​បាន​គាំទ្រ​សម្រាប់​តែ​កញ្ចប់​បាន​ដំឡើង​ក្នុង /system/app."</string>
     <string name="factorytest_no_action" msgid="872991874799998561">"រក​មិន​ឃើញ​កញ្ចប់​ដែល​ផ្ដល់​សកម្មភាព FACTORY_TEST ។"</string>
-    <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់​ផ្ដើម​ឡើង​វិញ"</string>
+    <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់​ផ្ដើម​ឡើង​វិញ​"</string>
     <string name="js_dialog_title" msgid="1987483977834603872">"ទំព័រ​មាន​ចំណងជើង \"<xliff:g id="TITLE">%s</xliff:g>\" សរសេរ៖"</string>
     <string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
     <string name="js_dialog_before_unload_title" msgid="2619376555525116593">"បញ្ជាក់​ការ​រុករក"</string>
@@ -1021,7 +1021,7 @@
     <string name="prepend_shortcut_label" msgid="2572214461676015642">"ម៉ឺនុយ +"</string>
     <string name="menu_space_shortcut_label" msgid="2410328639272162537">"ដកឃ្លា"</string>
     <string name="menu_enter_shortcut_label" msgid="2743362785111309668">"enter"</string>
-    <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"លុប"</string>
+    <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"លុប​"</string>
     <string name="search_go" msgid="8298016669822141719">"ស្វែងរក"</string>
     <string name="searchview_description_search" msgid="6749826639098512120">"ស្វែងរក"</string>
     <string name="searchview_description_query" msgid="5911778593125355124">"ស្វែងរក​សំណួរ"</string>
@@ -1105,18 +1105,18 @@
     <string name="preposition_for_date" msgid="9093949757757445117">"នៅ <xliff:g id="DATE">%s</xliff:g>"</string>
     <string name="preposition_for_time" msgid="5506831244263083793">"នៅ​ម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="preposition_for_year" msgid="5040395640711867177">"ក្នុង​ឆ្នាំ <xliff:g id="YEAR">%s</xliff:g>"</string>
-    <string name="day" msgid="8144195776058119424">"ថ្ងៃ"</string>
+    <string name="day" msgid="8144195776058119424">"ថ្ងៃ​"</string>
     <string name="days" msgid="4774547661021344602">"​ថ្ងៃ"</string>
     <string name="hour" msgid="2126771916426189481">"ម៉ោង"</string>
     <string name="hours" msgid="894424005266852993">"ម៉ោង"</string>
-    <string name="minute" msgid="9148878657703769868">"នាទី"</string>
+    <string name="minute" msgid="9148878657703769868">"នាទី​"</string>
     <string name="minutes" msgid="5646001005827034509">"នាទី"</string>
-    <string name="second" msgid="3184235808021478">"វិនាទី"</string>
+    <string name="second" msgid="3184235808021478">"វិនាទី​"</string>
     <string name="seconds" msgid="3161515347216589235">"វិនាទី"</string>
-    <string name="week" msgid="5617961537173061583">"សប្ដាហ៍"</string>
-    <string name="weeks" msgid="6509623834583944518">"សប្ដាហ៍"</string>
-    <string name="year" msgid="4001118221013892076">"ឆ្នាំ"</string>
-    <string name="years" msgid="6881577717993213522">"ឆ្នាំ"</string>
+    <string name="week" msgid="5617961537173061583">"សប្ដាហ៍​"</string>
+    <string name="weeks" msgid="6509623834583944518">"សប្ដាហ៍​"</string>
+    <string name="year" msgid="4001118221013892076">"ឆ្នាំ​"</string>
+    <string name="years" msgid="6881577717993213522">"ឆ្នាំ​"</string>
   <plurals name="duration_seconds">
     <item quantity="one" msgid="6962015528372969481">"1 វិនាទី"</item>
     <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> វិនាទី"</item>
@@ -1132,12 +1132,12 @@
     <string name="VideoView_error_title" msgid="3534509135438353077">"បញ្ហា​វីដេអូ"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"វីដេអូ​នេះ​មិន​ត្រឹមត្រូវ​សម្រាប់​​ចរន្ត​ចូល​ឧបករណ៍​នេះ។"</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"មិន​អាច​ចាក់​វីដេអូ​នេះ។"</string>
-    <string name="VideoView_error_button" msgid="2822238215100679592">"យល់​ព្រម"</string>
+    <string name="VideoView_error_button" msgid="2822238215100679592">"យល់​ព្រម​"</string>
     <string name="relative_time" msgid="1818557177829411417">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="noon" msgid="7245353528818587908">"រសៀល"</string>
     <string name="Noon" msgid="3342127745230013127">"រសៀល"</string>
     <string name="midnight" msgid="7166259508850457595">"កណ្ដាលអធ្រាត្រ"</string>
-    <string name="Midnight" msgid="5630806906897892201">"កណ្ដាល​អធ្រាត្រ"</string>
+    <string name="Midnight" msgid="5630806906897892201">"កណ្ដាល​អធ្រាត្រ​"</string>
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"ជ្រើស​ទាំងអស់"</string>
@@ -1154,13 +1154,13 @@
     <string name="inputMethod" msgid="1653630062304567879">"វិធីសាស្ត្រ​បញ្ចូល"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"សកម្មភាព​អត្ថបទ"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"អស់​ទំហំ​ផ្ទុក"</string>
-    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារ​ប្រព័ន្ធ​មួយ​ចំនួន​អាច​មិន​ដំណើរការ"</string>
+    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារ​ប្រព័ន្ធ​មួយ​ចំនួន​អាច​មិន​ដំណើរការ​"</string>
     <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុង​ដំណើរការ"</string>
     <string name="app_running_notification_text" msgid="4653586947747330058">"ប៉ះ​ ដើម្បី​មើល​ព័ត៌មាន​បន្ថែម ឬ​បញ្ឈប់​កម្មវិធី។"</string>
-    <string name="ok" msgid="5970060430562524910">"យល់​ព្រម"</string>
-    <string name="cancel" msgid="6442560571259935130">"បោះ​បង់"</string>
-    <string name="yes" msgid="5362982303337969312">"យល់​ព្រម"</string>
-    <string name="no" msgid="5141531044935541497">"បោះ​បង់"</string>
+    <string name="ok" msgid="5970060430562524910">"យល់​ព្រម​"</string>
+    <string name="cancel" msgid="6442560571259935130">"បោះ​បង់​"</string>
+    <string name="yes" msgid="5362982303337969312">"យល់​ព្រម​"</string>
+    <string name="no" msgid="5141531044935541497">"បោះ​បង់​"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"ប្រយ័ត្ន"</string>
     <string name="loading" msgid="7933681260296021180">"កំពុង​ផ្ទុក..."</string>
     <string name="capital_on" msgid="1544682755514494298">"បើក"</string>
@@ -1169,7 +1169,7 @@
     <string name="whichHomeApplication" msgid="4616420172727326782">"ជ្រើស​កម្មវិធី​ដើម"</string>
     <string name="alwaysUse" msgid="4583018368000610438">"ប្រើ​តាម​លំនាំដើម​សម្រាប់​សកម្មភាព​នេះ។"</string>
     <string name="clearDefaultHintMsg" msgid="3252584689512077257">"សម្អាត​លំនាំដើម​ក្នុង​ការកំណត់​ប្រព័ន្ធ &gt; កម្មវិធី &gt; ទាញ​យក។"</string>
-    <string name="chooseActivity" msgid="7486876147751803333">"ជ្រើស​សកម្មភាព"</string>
+    <string name="chooseActivity" msgid="7486876147751803333">"ជ្រើស​សកម្មភាព​​"</string>
     <string name="chooseUsbActivity" msgid="6894748416073583509">"ជ្រើស​កម្មវិធី​សម្រាប់​ឧបករណ៍​យូអេសប៊ី"</string>
     <string name="noApplications" msgid="2991814273936504689">"គ្មាន​កម្មវិធី​អាច​អនុវត្ត​សកម្មភាព​នេះ។"</string>
     <string name="aerr_title" msgid="1905800560317137752"></string>
@@ -1180,7 +1180,7 @@
     <string name="anr_activity_process" msgid="5776209883299089767">"សកម្មភាព <xliff:g id="ACTIVITY">%1$s</xliff:g> មិន​ឆ្លើយតប។\n\nតើ​អ្នក​ចង់​បិទ​វា?"</string>
     <string name="anr_application_process" msgid="8941757607340481057">"<xliff:g id="APPLICATION">%1$s</xliff:g> មិន​ឆ្លើយតប។ តើ​អ្នក​ចង់​បិទ​វា?"</string>
     <string name="anr_process" msgid="6513209874880517125">"ដំណើរការ <xliff:g id="PROCESS">%1$s</xliff:g> មិន​ឆ្លើយតប។ \n\nតើ​អ្នក​ចង់​បិទ​វា​ឬ?"</string>
-    <string name="force_close" msgid="8346072094521265605">"យល់​ព្រម"</string>
+    <string name="force_close" msgid="8346072094521265605">"យល់​ព្រម​"</string>
     <string name="report" msgid="4060218260984795706">"រាយការណ៍"</string>
     <string name="wait" msgid="7147118217226317732">"រង់ចាំ"</string>
     <string name="webpage_unresponsive" msgid="3272758351138122503">"ទំព័រ​ក្លាយ​ជា​មិន​ឆ្លើយតប។\n\nតើ​អ្នក​​ចង់​បិទ​វា?"</string>
@@ -1262,19 +1262,19 @@
     <string name="sms_short_code_details" msgid="3492025719868078457"><font fgcolor="#ffffb060">"នេះ​អាច​កាត់​លុយ"</font>" លើ​គណនី​ចល័ត​របស់​អ្នក។"</string>
     <string name="sms_premium_short_code_details" msgid="5523826349105123687"><font fgcolor="#ffffb060">"វា​នឹង​គិត​ថ្លៃ​សេវាកម្ម​លើ​គណនី​ចល័ត​របស់​អ្នក។"</font></string>
     <string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"ផ្ញើ"</string>
-    <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះ​បង់"</string>
+    <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះ​បង់​"</string>
     <string name="sms_short_code_remember_choice" msgid="5289538592272218136">"ចងចាំ​ជម្រើស​របស់​ខ្ញុំ"</string>
     <string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"អ្នក​អាច​ប្ដូរ​វា​ពេល​ក្រោយ​ក្នុង​ការ​កំណត់ &gt; កម្មវិធី"</string>
     <string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"អនុញ្ញាត​ជា​និច្ច"</string>
     <string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"កុំ​អនុញ្ញាត"</string>
     <string name="sim_removed_title" msgid="6227712319223226185">"បាន​ដក​ស៊ីម​កាត​ចេញ"</string>
-    <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញ​ចល័ត​នឹង​ប្រើ​លែង​បាន​រហូត​ដល់​អ្នក​ចាប់ផ្ដើម​ជា​មួយ​ស៊ីម​កាត​ដែល​បា​បញ្ចូល​ត្រឹមត្រូវ។"</string>
+    <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញ​ចល័ត​នឹង​ប្រើ​លែង​បាន​រហូត​ដល់​អ្នក​ចាប់ផ្ដើម​ជា​មួយ​ស៊ីម​កាត​ដែល​បា​បញ្ចូល​ត្រឹមត្រូវ។​"</string>
     <string name="sim_done_button" msgid="827949989369963775">"រួចរាល់"</string>
     <string name="sim_added_title" msgid="3719670512889674693">"បាន​បន្ថែម​ស៊ីម​កាត"</string>
     <string name="sim_added_message" msgid="6599945301141050216">"ចាប់ផ្ដើម​ឧបករណ៍​របស់​អ្នក​ឡើង​វិញ ដើម្បី​ចូល​ដំណើរការ​បណ្ដាញ​ចល័ត។"</string>
     <string name="sim_restart_button" msgid="4722407842815232347">"ចាប់ផ្ដើម​ឡើងវិញ"</string>
-    <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណត់​ម៉ោង"</string>
-    <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណត់​កាល​បរិច្ឆេទ"</string>
+    <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណត់​ម៉ោង​"</string>
+    <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណត់​កាល​បរិច្ឆេទ​"</string>
     <string name="date_time_set" msgid="5777075614321087758">"កំណត់"</string>
     <string name="date_time_done" msgid="2507683751759308828">"រួចរាល់"</string>
     <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"ថ្មី៖ "</font></string>
@@ -1352,7 +1352,7 @@
     <string name="permdesc_copyProtectedData" msgid="4390697124288317831">"ឲ្យ​កម្មវិធី​ដក​សេវាកម្ម​នៃ​កម្មវិធី​ផ្ទុក​​លំនាំដើម ដើម្បី​ចម្លង​មាតិកា។​ មិន​សម្រាប់​ប្រើ​ដោយ​កម្មវិធី​លំនាំដើម។"</string>
     <string name="permlab_route_media_output" msgid="1642024455750414694">"នាំ​ផ្លូវ​លទ្ធផល​មេឌៀ"</string>
     <string name="permdesc_route_media_output" msgid="4932818749547244346">"ឲ្យ​កម្មវិធី​នាំ​ផ្លូវ​លទ្ធផល​មេឌៀ​ទៅ​ឧបករណ៍​​ខាង​ក្រៅ​ផ្សេង។"</string>
-    <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូល​ដំណើរការ​ឧបករណ៍​ផ្ទុក​សុវត្ថិភាព"</string>
+    <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូល​ដំណើរការ​ឧបករណ៍​ផ្ទុក​សុវត្ថិភាព​"</string>
     <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"ឲ្យ​កម្មវិធី​ចូល​​ការ​ផ្ទុក​មាន​សុវត្ថិភាព keguard ។"</string>
     <string name="permlab_control_keyguard" msgid="172195184207828387">"ពិនិត្យ​ការ​បង្ហាញ និង​លាក់​ការ​ការពារ"</string>
     <string name="permdesc_control_keyguard" msgid="3043732290518629061">"ឲ្យ​កម្មវិធី​គ្រប់គ្រង keguard ។"</string>
@@ -1367,7 +1367,7 @@
     <string name="ime_action_go" msgid="8320845651737369027">"ទៅ"</string>
     <string name="ime_action_search" msgid="658110271822807811">"ស្វែងរក"</string>
     <string name="ime_action_send" msgid="2316166556349314424">"ផ្ញើ"</string>
-    <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់"</string>
+    <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់​"</string>
     <string name="ime_action_done" msgid="8971516117910934605">"រួចរាល់"</string>
     <string name="ime_action_previous" msgid="1443550039250105948">"មុន"</string>
     <string name="ime_action_default" msgid="2840921885558045721">"អនុវត្ត"</string>
@@ -1376,7 +1376,7 @@
     <string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"កម្មវិធី​មួយ ឬ​ច្រើន​ដូច​ខាង​ក្រោម​ស្នើ​សិទ្ធិ ដើម្បី​ចូល​គណនី​របស់​អ្នក​ឥឡូវ និង​ពេល​អនាគត។"</string>
     <string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"តើ​អ្នក​ចង់​អនុញ្ញាត​សំណើ​នេះ?"</string>
     <string name="grant_permissions_header_text" msgid="6874497408201826708">"ស្នើ​ចូល"</string>
-    <string name="allow" msgid="7225948811296386551">"អនុញ្ញាត"</string>
+    <string name="allow" msgid="7225948811296386551">"អនុញ្ញាត​"</string>
     <string name="deny" msgid="2081879885755434506">"បដិសេធ"</string>
     <string name="permission_request_notification_title" msgid="6486759795926237907">"បាន​ស្នើ​សិទ្ធិ"</string>
     <string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"បាន​ស្នើ​សិទ្ធិ\nសម្រាប់​គណនី <xliff:g id="ACCOUNT">%s</xliff:g> ។"</string>
@@ -1399,12 +1399,12 @@
     <string name="no_file_chosen" msgid="6363648562170759465">"គ្មាន​ឯកសារ​បាន​ជ្រើស"</string>
     <string name="reset" msgid="2448168080964209908">"កំណត់​ឡើងវិញ"</string>
     <string name="submit" msgid="1602335572089911941">"ដាក់​ស្នើ"</string>
-    <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"បាន​បើក​របៀប​រថយន្ត"</string>
+    <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"បាន​បើក​របៀប​រថយន្ត​"</string>
     <string name="car_mode_disable_notification_message" msgid="8035230537563503262">"ប៉ះ​ ដើម្បី​ចេញ​ពី​របៀប​រថយន្ត​។"</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"ភ្ជាប់ ឬ​ហតស្ពត​សកម្ម"</string>
     <string name="tethered_notification_message" msgid="6857031760103062982">"ប៉ះ​ ដើម្បី​រៀបចំ។"</string>
     <string name="back_button_label" msgid="2300470004503343439">"ថយក្រោយ"</string>
-    <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់​"</string>
     <string name="skip_button_label" msgid="1275362299471631819">"រំលង"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"ការ​ប្រើ​ទិន្នន័យ​ចល័ត​ខ្ពស់"</string>
     <string name="throttle_warning_notification_message" msgid="3340822228599337743">"ប៉ះ​ ដើម្បី​​ស្វែងយល់​បន្ថែម​អំពី​ការ​ប្រើ​​​ទិន្នន័យ​ចល័ត​។"</string>
@@ -1430,7 +1430,7 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"ឧបករណ៍​ផ្ទុក​យូអេសប៊ី​បច្ចុប្បន្ន​កំពុង​ប្រើ​ដោយ​កុំព្យូទ័រ។"</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"បច្ចុប្បន្ន​កាត​អេសឌី​កំពុង​ប្រើ​ដោយ​កុំព្យូទ័រ"</string>
     <string name="media_unknown_state" msgid="729192782197290385">"មិន​ស្គាល់​ស្ថានភាព​មេឌៀ​ខាង​ក្រៅ។"</string>
-    <string name="share" msgid="1778686618230011964">"ចែក​រំលែក"</string>
+    <string name="share" msgid="1778686618230011964">"ចែក​រំលែក​"</string>
     <string name="find" msgid="4808270900322985960">"រក"</string>
     <string name="websearch" msgid="4337157977400211589">"ស្វែងរក​តាម​បណ្ដាញ"</string>
     <string name="find_next" msgid="5742124618942193978">"រក​បន្ទាប់"</string>
@@ -1446,7 +1446,7 @@
     <string name="sync_undo_deletes" msgid="2941317360600338602">"មិន​ធ្វើ​ការ​លុប​វិញ"</string>
     <string name="sync_do_nothing" msgid="3743764740430821845">"មិន​ធ្វើអ្វី​ទេ​ឥឡូវ"</string>
     <string name="choose_account_label" msgid="5655203089746423927">"ជ្រើស​គណនី"</string>
-    <string name="add_account_label" msgid="2935267344849993553">"បន្ថែម​គណនី​ថ្មី"</string>
+    <string name="add_account_label" msgid="2935267344849993553">"បន្ថែម​គណនី​ថ្មី​​"</string>
     <string name="add_account_button_label" msgid="3611982894853435874">"បន្ថែម​គណនី"</string>
     <string name="number_picker_increment_button" msgid="2412072272832284313">"បង្កើន"</string>
     <string name="number_picker_decrement_button" msgid="476050778386779067">"បន្ថយ"</string>
@@ -1465,15 +1465,15 @@
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"បង្កើន​​ឆ្នាំ"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"បន្ថយ​ឆ្នាំ"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
-    <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់"</string>
+    <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់​"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string>
     <string name="keyboardview_keycode_done" msgid="1992571118466679775">"រួចរាល់"</string>
     <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ប្ដូរ​របៀប"</string>
     <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
     <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
-    <string name="activitychooserview_choose_application" msgid="2125168057199941199">"ជ្រើស​កម្មវិធី"</string>
+    <string name="activitychooserview_choose_application" msgid="2125168057199941199">"ជ្រើស​កម្មវិធី​​"</string>
     <string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"មិន​អាច​ចាប់ផ្ដើម <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="shareactionprovider_share_with" msgid="806688056141131819">"ចែករំលែក​ជា​មួយ"</string>
+    <string name="shareactionprovider_share_with" msgid="806688056141131819">"ចែករំលែក​ជា​មួយ​"</string>
     <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"ចែក​រំលែក​ជា​មួយ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="content_description_sliding_handle" msgid="415975056159262248">"គ្រប់គ្រង​ការ​រុញ។ ប៉ះ &amp; សង្កត់។"</string>
     <string name="description_target_unlock_tablet" msgid="3833195335629795055">"អូស​ ដើម្បី​ដោះ​សោ។"</string>
@@ -1487,7 +1487,7 @@
     <string name="storage_internal" msgid="4891916833657929263">"ឧបករណ៍​ផ្ទុក​ខាង​ក្នុង"</string>
     <string name="storage_sd_card" msgid="3282948861378286745">"កាត​អេសឌី"</string>
     <string name="storage_usb" msgid="3017954059538517278">"ឧបករណ៍​ផ្ទុក​យូអេសប៊ី"</string>
-    <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល"</string>
+    <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល​"</string>
     <string name="data_usage_warning_title" msgid="1955638862122232342">"ការព្រមាន​ប្រើ​ទិន្នន័យ"</string>
     <string name="data_usage_warning_body" msgid="2814673551471969954">"ប៉ះ ដើម្បី​មើល​ការ​ប្រើ និង​ការ​កំណត់។"</string>
     <string name="data_usage_3g_limit_title" msgid="7093334419518706686">"បាន​បិទ​ទិន្នន័យ 2G​-3G"</string>
@@ -1544,7 +1544,7 @@
     <string name="media_route_status_available" msgid="6983258067194649391">"ទំនេរ"</string>
     <string name="media_route_status_not_available" msgid="6739899962681886401">"មិន​ទំនេរ"</string>
     <string name="media_route_status_in_use" msgid="4533786031090198063">"កំពុង​ប្រើ"</string>
-    <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អេក្រង់​ជាប់"</string>
+    <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អេក្រង់​ជាប់​"</string>
     <string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"អេក្រង់ HDMI"</string>
     <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"#<xliff:g id="ID">%1$d</xliff:g> ត្រួត​គ្នា"</string>
     <string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string>
@@ -1576,7 +1576,7 @@
     <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ព្យាយាម​លំនាំ​ច្រើន​ពេក"</string>
     <string name="kg_login_instructions" msgid="1100551261265506448">"ដើម្បី​ដោះ​សោ ចូល​ក្នុង​គណនី Google ។"</string>
     <string name="kg_login_username_hint" msgid="5718534272070920364">"ឈ្មោះ​អ្នក​ប្រើ (អ៊ី​ម៉ែ​ល​)"</string>
-    <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
+    <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់​"</string>
     <string name="kg_login_submit_button" msgid="5355904582674054702">"ចូល"</string>
     <string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​មិន​ត្រឹមត្រូវ។"</string>
     <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លេច​ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​របស់​អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
@@ -1685,7 +1685,7 @@
     <string name="mediasize_japanese_you4" msgid="2091777168747058008">"You4"</string>
     <string name="mediasize_unknown_portrait" msgid="3088043641616409762">"​មិន​ស្គាល់​បញ្ឈរ"</string>
     <string name="mediasize_unknown_landscape" msgid="4876995327029361552">"មិន​ស្គាល់​ទេសភាព"</string>
-    <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បាន​បោះ​បង់"</string>
+    <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បាន​បោះ​បង់​"</string>
     <string name="write_fail_reason_cannot_write" msgid="8132505417935337724">"កំហុស​ក្នុង​ការ​សរសេរ​មាតិកា"</string>
     <string name="reason_unknown" msgid="6048913880184628119">"មិន​ស្គាល់"</string>
     <string name="reason_service_unavailable" msgid="7824008732243903268">"មិន​បា​ន​បើក​សេវាកម្ម​បោះពុម្ព"</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index 08ba7e1..7ec1376 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -1697,7 +1697,7 @@
     <string name="restr_pin_enter_old_pin" msgid="1462206225512910757">"PIN ປະ​ຈຸ​ບັນ"</string>
     <string name="restr_pin_enter_new_pin" msgid="5959606691619959184">"ລະຫັດ PIN ໃໝ່"</string>
     <string name="restr_pin_confirm_pin" msgid="8501523829633146239">"ຢືນຢັນລະຫັດ PIN ໃໝ່"</string>
-    <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສໍາ​ລັບ​ການ​ປັບ​ປຸງ​ຂໍ້ຈໍາ​ກັດ"</string>
+    <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສໍາ​ລັບ​ການ​ປັບ​ປຸງ​ຂໍ້ຈໍາ​ກັດ​"</string>
     <string name="restr_pin_error_doesnt_match" msgid="2224214190906994548">"PIN ບໍ່​ກົງກັນ. ລອງໃໝ່ອີກຄັ້ງ​."</string>
     <string name="restr_pin_error_too_short" msgid="8173982756265777792">"PIN ​ສັ້ນ​ເກີນ​ໄປ​. ຕ້ອງມີຢ່າງໜ້ອຍ 4 ຫຼັກ​."</string>
   <plurals name="restr_pin_countdown">
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index cedb92d..c3ad731 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -955,18 +955,33 @@
         <!-- ============= -->
         <!-- Color palette -->
         <!-- ============= -->
-        <attr name="colorPrimaryDark" format="color" />
+        <eat-comment />
+
+        <!-- The primary branding color for the app. By default, this is the color applied to the
+             action bar background and framework controls (via colorControlActivated). -->
         <attr name="colorPrimary" format="color" />
+
+        <!-- Dark variant of the primary branding color. By default, this is the color applied to
+             the status bar (via statusBarColor) and navigation bar (via navigationBarColor). -->
+        <attr name="colorPrimaryDark" format="color" />
+
+        <!-- Light variant of the primary branding color. TODO: Not used? -->
         <attr name="colorPrimaryLight" format="color" />
+
+        <!-- Bright complement to the primary branding color. TODO: Not used? -->
         <attr name="colorAccent" format="color" />
 
+        <!-- The color applied to framework controls in their normal state. -->
         <attr name="colorControlNormal" format="color" />
+
+        <!-- The color applied to framework controls in their activated (ex. checked) state. -->
         <attr name="colorControlActivated" format="color" />
 
+        <!-- The color applied to framework buttons in their normal state. -->
         <attr name="colorButtonNormal" format="color" />
+
+        <!-- The color applied to framework buttons in their pressed state. -->
         <attr name="colorButtonPressed" format="color" />
-        <attr name="colorButtonNormalColored" format="color" />
-        <attr name="colorButtonPressedColored" format="color" />
     </declare-styleable>
 
     <!-- **************************************************************** -->
@@ -2355,11 +2370,9 @@
              view with a theme override will inherit the themed context. -->
         <attr name="theme" />
 
-        <!-- Specifies that the shared name of the View to be shared with another Activity.
-             When transitioning between Activities, the name links a UI element in the starting
-             Activity to UI element in the called Activity. Names should be unique in the
-             View hierarchy. -->
-        <attr name="sharedElementName" format="string" />
+        <!-- Names a View such that it can be identified for Transitions. Names should be
+             unique in the View hierarchy. -->
+        <attr name="viewName" format="string" />
 
         <!-- Specifies that this view should permit nested scrolling within a compatible
              ancestor view. -->
@@ -5022,10 +5035,14 @@
         <attr name="targetId" format="reference" />
         <!-- The id of a target to exclude from this transition. -->
         <attr name="excludeId" format="reference" />
-        <!-- The fully-qualified name of the Class to exclude from this transition. -->
-        <attr name="excludeClass" format="string" />
         <!-- The fully-qualified name of the Class to include in this transition. -->
         <attr name="targetClass" />
+        <!-- The fully-qualified name of the Class to exclude from this transition. -->
+        <attr name="excludeClass" format="string" />
+        <!-- The viewName of the target on which this transition will animation changes. -->
+        <attr name="targetViewName" format="string" />
+        <!-- The viewName of the target to exclude from this transition. -->
+        <attr name="excludeViewName" format="string" />
     </declare-styleable>
 
     <!-- Use <code>set</code> as the root tag of the XML resource that
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 889c368..6ea0fae 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -82,7 +82,6 @@
   <item type="id" name="action_bar_spinner" />
   <item type="id" name="current_scene" />
   <item type="id" name="scene_layoutid_cache" />
-  <item type="id" name="shared_element_name" />
   <item type="id" name="mask" />
   <item type="id" name="shared_element" />
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index dc5efea..3ef4ab6 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2114,7 +2114,7 @@
   <public type="attr" name="controlY1" />
   <public type="attr" name="controlX2" />
   <public type="attr" name="controlY2" />
-  <public type="attr" name="sharedElementName" />
+  <public type="attr" name="viewName" />
   <public type="attr" name="transitionGroup" />
   <public type="attr" name="viewportWidth" />
   <public type="attr" name="viewportHeight" />
@@ -2136,8 +2136,6 @@
   <public type="attr" name="colorControlActivated" />
   <public type="attr" name="colorButtonNormal" />
   <public type="attr" name="colorButtonPressed" />
-  <public type="attr" name="colorButtonNormalColored" />
-  <public type="attr" name="colorButtonPressedColored" />
   <public type="attr" name="persistable" />
   <public type="attr" name="titleTextAppearance" />
   <public type="attr" name="subtitleTextAppearance" />
@@ -2170,6 +2168,8 @@
   <public type="attr" name="fromId" />
   <public type="attr" name="reversible" />
   <public type="attr" name="splitTrack" />
+  <public type="attr" name="targetViewName" />
+  <public type="attr" name="excludeViewName" />
 
   <public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
 
@@ -2178,7 +2178,6 @@
 
   <public-padding type="id" name="l_resource_pad" end="0x01020040" />
 
-  <public type="id" name="shared_element_name" />
   <public type="id" name="mask" />
   <public type="id" name="shared_element" />
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 97400b2..8286ef9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -953,10 +953,11 @@
         Malicious apps may use this to forge MMS message receipt or to
         silently replace the content of any webpage with malicious variants.</string>
 
+    <!-- TODO: Mark these as translatable when API is finalized. -->
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_broadcastScoreNetworks">send score networks broadcast</string>
+    <string name="permlab_broadcastScoreNetworks" translatable="false">send score networks broadcast</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_broadcastScoreNetworks">Allows the app
+    <string name="permdesc_broadcastScoreNetworks" translatable="false">Allows the app
         to broadcast a notification that networks need to be scored.
         Never needed for normal apps.
     </string>
diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml
index 18d6f80..6f93c829 100644
--- a/core/res/res/values/themes_quantum.xml
+++ b/core/res/res/values/themes_quantum.xml
@@ -377,9 +377,6 @@
         <item name="colorControlActivated">?attr/colorPrimary</item>
         <item name="colorButtonNormal">@color/quantum_grey_700</item>
         <item name="colorButtonPressed">@color/quantum_grey_500</item>
-        <!-- TODO: Remove these attrs and move into button style. -->
-        <item name="colorButtonNormalColored">?attr/colorPrimary</item>
-        <item name="colorButtonPressedColored">?attr/colorPrimaryLight</item>
     </style>
 
     <!-- Quantum Paper theme (light version). -->
diff --git a/docs/html/tools/revisions/build-tools.jd b/docs/html/tools/revisions/build-tools.jd
index c3c83ef..d8abab0 100644
--- a/docs/html/tools/revisions/build-tools.jd
+++ b/docs/html/tools/revisions/build-tools.jd
@@ -77,6 +77,26 @@
 <div class="toggle-content opened">
   <p><a href="#" onclick="return toggleContent(this)">
     <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+      alt=""/>Build Tools, Revision 19.1.0</a> <em>(May 2014)</em>
+  </p>
+  <div class="toggle-content-toggleme">
+
+    <dl>
+      <dt>General Notes:</dt>
+      <dd>
+        <ul>
+          <li>Added <code>zipalign</code> to the Build Tools.</li>
+          <li>Modified <code>aapt</code> to ignore XML files that fail to compile.</li>
+        </ul>
+      </dd>
+    </dl>
+
+  </div>
+</div>
+
+<div class="toggle-content closed">
+  <p><a href="#" onclick="return toggleContent(this)">
+    <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
       alt=""/>Build Tools, Revision 19.0.3</a> <em>(March 2014)</em>
   </p>
   <div class="toggle-content-toggleme">
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 6a3003e..a033f86 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -199,13 +199,18 @@
     return -1;
 }
 
+static uint8_t to_uint8(float value) {
+    int c = (int) (value + .5f);
+    return static_cast<uint8_t>( c < 0 ? 0 : c > 255 ? 255 : c );
+}
+
 void CanvasPropertyPaintAnimator::setValue(float value) {
     switch (mField) {
     case STROKE_WIDTH:
         mProperty->value.setStrokeWidth(value);
         return;
     case ALPHA:
-        mProperty->value.setAlpha(value);
+        mProperty->value.setAlpha(to_uint8(value));
         return;
     }
     LOG_ALWAYS_FATAL("Unknown field %d", (int) mField);
diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp
index b56648e..1f84b86 100644
--- a/libs/hwui/Interpolator.cpp
+++ b/libs/hwui/Interpolator.cpp
@@ -18,7 +18,7 @@
 
 #include "Interpolator.h"
 
-#include <math.h>
+#include <cmath>
 #include <cutils/log.h>
 
 #include "utils/MathUtils.h"
@@ -34,6 +34,62 @@
     return (float)(cosf((input + 1) * M_PI) / 2.0f) + 0.5f;
 }
 
+float AccelerateInterpolator::interpolate(float input) {
+    if (mFactor == 1.0f) {
+        return input * input;
+    } else {
+        return pow(input, mDoubleFactor);
+    }
+}
+
+float AnticipateInterpolator::interpolate(float t) {
+    return t * t * ((mTension + 1) * t - mTension);
+}
+
+static float a(float t, float s) {
+    return t * t * ((s + 1) * t - s);
+}
+
+static float o(float t, float s) {
+    return t * t * ((s + 1) * t + s);
+}
+
+float AnticipateOvershootInterpolator::interpolate(float t) {
+    if (t < 0.5f) return 0.5f * a(t * 2.0f, mTension);
+    else return 0.5f * (o(t * 2.0f - 2.0f, mTension) + 2.0f);
+}
+
+static float bounce(float t) {
+    return t * t * 8.0f;
+}
+
+float BounceInterpolator::interpolate(float t) {
+    t *= 1.1226f;
+    if (t < 0.3535f) return bounce(t);
+    else if (t < 0.7408f) return bounce(t - 0.54719f) + 0.7f;
+    else if (t < 0.9644f) return bounce(t - 0.8526f) + 0.9f;
+    else return bounce(t - 1.0435f) + 0.95f;
+}
+
+float CycleInterpolator::interpolate(float input) {
+    return sinf(2 * mCycles * M_PI * input);
+}
+
+float DecelerateInterpolator::interpolate(float input) {
+    float result;
+    if (mFactor == 1.0f) {
+        result = 1.0f - (1.0f - input) * (1.0f - input);
+    } else {
+        result = 1.0f - pow((1.0f - input), 2 * mFactor);
+    }
+    return result;
+}
+
+float OvershootInterpolator::interpolate(float t) {
+    t -= 1.0f;
+    return t * t * ((mTension + 1) * t + mTension) + 1.0f;
+}
+
 LUTInterpolator::LUTInterpolator(float* values, size_t size) {
     mValues = values;
     mSize = size;
diff --git a/libs/hwui/Interpolator.h b/libs/hwui/Interpolator.h
index 44fb37c..dfa0a85 100644
--- a/libs/hwui/Interpolator.h
+++ b/libs/hwui/Interpolator.h
@@ -37,12 +37,68 @@
 
 class ANDROID_API AccelerateDecelerateInterpolator : public Interpolator {
 public:
-    AccelerateDecelerateInterpolator() {}
-    virtual ~AccelerateDecelerateInterpolator() {}
-
     virtual float interpolate(float input);
 };
 
+class ANDROID_API AccelerateInterpolator : public Interpolator {
+public:
+    AccelerateInterpolator(float factor) : mFactor(factor), mDoubleFactor(factor*2) {}
+    virtual float interpolate(float input);
+private:
+    const float mFactor;
+    const float mDoubleFactor;
+};
+
+class ANDROID_API AnticipateInterpolator : public Interpolator {
+public:
+    AnticipateInterpolator(float tension) : mTension(tension) {}
+    virtual float interpolate(float input);
+private:
+    const float mTension;
+};
+
+class ANDROID_API AnticipateOvershootInterpolator : public Interpolator {
+public:
+    AnticipateOvershootInterpolator(float tension) : mTension(tension) {}
+    virtual float interpolate(float input);
+private:
+    const float mTension;
+};
+
+class ANDROID_API BounceInterpolator : public Interpolator {
+public:
+    virtual float interpolate(float input);
+};
+
+class ANDROID_API CycleInterpolator : public Interpolator {
+public:
+    CycleInterpolator(float cycles) : mCycles(cycles) {}
+    virtual float interpolate(float input);
+private:
+    const float mCycles;
+};
+
+class ANDROID_API DecelerateInterpolator : public Interpolator {
+public:
+    DecelerateInterpolator(float factor) : mFactor(factor) {}
+    virtual float interpolate(float input);
+private:
+    const float mFactor;
+};
+
+class ANDROID_API LinearInterpolator : public Interpolator {
+public:
+    virtual float interpolate(float input) { return input; }
+};
+
+class ANDROID_API OvershootInterpolator : public Interpolator {
+public:
+    OvershootInterpolator(float tension) : mTension(tension) {}
+    virtual float interpolate(float input);
+private:
+    const float mTension;
+};
+
 class ANDROID_API LUTInterpolator : public Interpolator {
 public:
     LUTInterpolator(float* values, size_t size);
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 5a23158..5754536 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -98,6 +98,8 @@
 
     bool enableDirtyRegions(EGLSurface surface);
 
+    void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize);
+
 private:
     GlobalContext();
     // GlobalContext is never destroyed, method is purposely not implemented
@@ -118,6 +120,10 @@
     bool mCanSetDirtyRegions;
 
     EGLSurface mCurrentSurface;
+
+    sp<GraphicBuffer> mAtlasBuffer;
+    int64_t* mAtlasMap;
+    size_t mAtlasMapSize;
 };
 
 GlobalContext* GlobalContext::sContext = 0;
@@ -135,7 +141,9 @@
         , mEglContext(EGL_NO_CONTEXT)
         , mPBufferSurface(EGL_NO_SURFACE)
         , mRequestDirtyRegions(load_dirty_regions_property())
-        , mCurrentSurface(EGL_NO_SURFACE) {
+        , mCurrentSurface(EGL_NO_SURFACE)
+        , mAtlasMap(NULL)
+        , mAtlasMapSize(0) {
     mCanSetDirtyRegions = mRequestDirtyRegions;
     ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false");
 }
@@ -201,9 +209,28 @@
         "Failed to create context, error = %s", egl_error_str());
 }
 
+void GlobalContext::setTextureAtlas(const sp<GraphicBuffer>& buffer,
+        int64_t* map, size_t mapSize) {
+
+    // Already initialized
+    if (mAtlasBuffer.get()) {
+        ALOGW("Multiple calls to setTextureAtlas!");
+        delete map;
+        return;
+    }
+
+    mAtlasBuffer = buffer;
+    mAtlasMap = map;
+    mAtlasMapSize = mapSize;
+
+    if (hasContext()) {
+        usePBufferSurface();
+        initAtlas();
+    }
+}
+
 void GlobalContext::initAtlas() {
-    // TODO implement
-    // For now just run without an atlas
+    Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize);
 }
 
 void GlobalContext::usePBufferSurface() {
@@ -533,6 +560,11 @@
     }
 }
 
+void CanvasContext::setTextureAtlas(const sp<GraphicBuffer>& buffer,
+        int64_t* map, size_t mapSize) {
+    GlobalContext::get()->setTextureAtlas(buffer, map, mapSize);
+}
+
 } /* namespace renderthread */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index dcb9858..a793d42 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -70,6 +70,9 @@
     Layer* createRenderLayer(int width, int height);
     Layer* createTextureLayer();
 
+    ANDROID_API static void setTextureAtlas(const sp<GraphicBuffer>& buffer,
+            int64_t* map, size_t mapSize);
+
 private:
     void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
     void prepareTree(TreeInfo& info);
diff --git a/media/tests/MediaFrameworkTest/Android.mk b/media/tests/MediaFrameworkTest/Android.mk
index 1e6b2e7..42da48d 100644
--- a/media/tests/MediaFrameworkTest/Android.mk
+++ b/media/tests/MediaFrameworkTest/Android.mk
@@ -7,7 +7,7 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
-LOCAL_STATIC_JAVA_LIBRARIES := easymocklib mockito-target
+LOCAL_STATIC_JAVA_LIBRARIES := easymocklib mockito-target core-tests android-support-test
 
 LOCAL_PACKAGE_NAME := mediaframeworktest
 
diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index 91ee2c6..c62199f 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -76,4 +76,8 @@
          android:label="MediaFramework integration tests InstrumentationRunner">
      </instrumentation>
 
+     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+         android:targetPackage="com.android.mediaframeworktest"
+         android:label="media framework tests"/>
+
 </manifest>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
index 64b12b7..11d9070 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
@@ -63,6 +63,7 @@
         suite.addTestSuite(CameraUtilsRuntimeExceptionTest.class);
         suite.addTestSuite(CameraUtilsUncheckedThrowTest.class);
         suite.addTestSuite(CameraUtilsBinderDecoratorTest.class);
+        suite.addTestSuite(CameraUtilsTypeReferenceTest.class);
         suite.addTestSuite(CameraMetadataTest.class);
     }
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ByteArrayHelpers.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ByteArrayHelpers.java
new file mode 100644
index 0000000..6f31480
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ByteArrayHelpers.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.unit;
+
+import android.util.Log;
+
+import java.lang.reflect.Array;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+public class ByteArrayHelpers {
+
+    private static final String TAG = "ByteArrayHelpers";
+    private static boolean VERBOSE = false;
+
+    /**
+     * Convert an array of byte primitives to a {@code byte[]} using native endian order.
+     *
+     * <p>This function is a pass-through; it's here only to provide overloads for
+     * every single type of primitive arrays.
+     *
+     * @param array array of primitives
+     * @return array
+     */
+    public static byte[] toByteArray(byte[] array) {
+        return array;
+    }
+
+    /**
+     * Convert an array of shorts to a {@code byte[]} using native endian order.
+     *
+     * @param array array of shorts
+     * @return array converted into byte array using native endian order
+     */
+    public static byte[] toByteArray(short[] array) {
+        return toByteArray(array, Short.SIZE);
+    }
+
+    /**
+     * Convert an array of chars to a {@code byte[]} using native endian order.
+     *
+     * @param array array of chars
+     * @return array converted into byte array using native endian order
+     */
+    public static byte[] toByteArray(char[] array) {
+        return toByteArray(array, Character.SIZE);
+    }
+    /**
+     * Convert an array of ints to a {@code byte[]} using native endian order.
+     *
+     * @param array array of ints
+     * @return array converted into byte array using native endian order
+     */
+    public static byte[] toByteArray(int[] array) {
+        return toByteArray(array, Integer.SIZE);
+    }
+    /**
+     * Convert an array of longs to a {@code byte[]} using native endian order.
+     *
+     * @param array array of longs
+     * @return array converted into byte array using native endian order
+     */
+    public static byte[] toByteArray(long[] array) {
+        return toByteArray(array, Long.SIZE);
+    }
+    /**
+     * Convert an array of floats to a {@code byte[]} using native endian order.
+     *
+     * @param array array of floats
+     * @return array converted into byte array using native endian order
+     */
+    public static byte[] toByteArray(float[] array) {
+        return toByteArray(array, Float.SIZE);
+    }
+    /**
+     * Convert an array of doubles to a {@code byte[]} using native endian order.
+     *
+     * @param array array of doubles
+     * @return array converted into byte array using native endian order
+     */
+    public static byte[] toByteArray(double[] array) {
+        return toByteArray(array, Double.SIZE);
+    }
+
+    /**
+     * Convert an array of primitives to a {@code byte[]} using native endian order.
+     *
+     * <p>Arguments other than arrays are not supported. The array component must be primitive,
+     * the wrapper class is not allowed (e.g. {@code int[]} is ok, but {@code Integer[]} is not.</p>
+     *
+     * @param array array of primitives
+     * @return array converted into byte array using native endian order
+     *
+     * @throws IllegalArgumentException if {@code array} was not an array of primitives
+     */
+    public static <T> byte[] toByteArray(T array) {
+        @SuppressWarnings("unchecked")
+        Class<T> klass = (Class<T>) array.getClass();
+
+        if (!klass.isArray()) {
+            throw new IllegalArgumentException("array class must be an array");
+        }
+
+        Class<?> componentClass = klass.getComponentType();
+
+        if (!componentClass.isPrimitive()) {
+            throw new IllegalArgumentException("array's component must be a primitive");
+        }
+
+        int sizeInBits;
+        if (klass == int.class) {
+            sizeInBits = Integer.SIZE;
+        } else if (klass == float.class) {
+            sizeInBits = Float.SIZE;
+        } else if (klass == double.class) {
+            sizeInBits = Double.SIZE;
+        } else if (klass == short.class) {
+            sizeInBits = Short.SIZE;
+        } else if (klass == char.class) {
+            sizeInBits = Character.SIZE;
+        } else if (klass == long.class) {
+            sizeInBits = Long.SIZE;
+        } else if (klass == byte.class) {
+            sizeInBits = Byte.SIZE;
+        } else {
+            throw new AssertionError();
+        }
+
+        return toByteArray(array, sizeInBits);
+    }
+
+    /**
+     * Convert a variadic list of {@code Number}s into a byte array using native endian order.
+     *
+     * <p>Each {@link Number} must be an instance of a primitive wrapper class
+     * (e.g. {@link Integer} is OK, since it wraps {@code int}, but {@code BigInteger} is not.</p>
+     *
+     * @param numbers variadic list of numeric values
+     * @return array converted into byte array using native endian order
+     *
+     * @throws IllegalArgumentException
+     *          if {@code numbers} contained a class that wasn't a primitive wrapper
+     */
+    public static byte[] toByteArray(Number... numbers) {
+        if (numbers.length == 0) {
+            throw new IllegalArgumentException("too few numbers");
+        }
+
+        if (VERBOSE) Log.v(TAG, "toByteArray - input: " + Arrays.toString(numbers));
+
+        // Have a large enough capacity to fit in every number as a double
+        ByteBuffer byteBuffer = ByteBuffer.allocate(numbers.length * (Double.SIZE / Byte.SIZE))
+                .order(ByteOrder.nativeOrder());
+
+        for (int i = 0; i < numbers.length; ++i) {
+            Number value = numbers[i];
+            Class<? extends Number> klass = value.getClass();
+
+            if (VERBOSE) Log.v(TAG, "toByteArray - number " + i + ", class " + klass);
+
+            if (klass == Integer.class) {
+                byteBuffer.putInt((Integer)value);
+            } else if (klass == Float.class) {
+                byteBuffer.putFloat((Float)value);
+            } else if (klass == Double.class) {
+                byteBuffer.putDouble((Double)value);
+            } else if (klass == Short.class) {
+                byteBuffer.putShort((Short)value);
+            } else if (klass == Long.class) {
+                byteBuffer.putLong((Long)value);
+            } else if (klass == Byte.class) {
+                byteBuffer.put((Byte)value);
+            } else {
+                throw new IllegalArgumentException(
+                        "number class invalid; must be wrapper around primitive class");
+            }
+        }
+
+        if (VERBOSE) Log.v(TAG, "toByteArray - end of loop");
+
+        // Each number written is at least 1 byte, so the position should be at least length
+        if (numbers.length != 0 && byteBuffer.position() < numbers.length) {
+            throw new AssertionError(String.format(
+                    "Had %d numbers, but byte buffer position was only %d",
+                    numbers.length, byteBuffer.position()));
+        }
+
+        byteBuffer.flip();
+
+        byte[] bytes = new byte[byteBuffer.limit()];
+        byteBuffer.get(bytes);
+
+        if (VERBOSE) Log.v(TAG, "toByteArray - output: " + Arrays.toString(bytes));
+
+        return bytes;
+    }
+
+    private static <T> byte[] toByteArray(T array, int sizeOfTBits) {
+        @SuppressWarnings("unchecked")
+        Class<T> klass = (Class<T>) array.getClass();
+
+        if (!klass.isArray()) {
+            throw new IllegalArgumentException("array class must be an array");
+        }
+
+        int sizeOfT = sizeOfTBits / Byte.SIZE;
+        int byteLength = Array.getLength(array) * sizeOfT;
+
+        if (klass == byte[].class) {
+            // Always return a copy
+            return Arrays.copyOf((byte[])array, byteLength);
+        }
+
+        ByteBuffer byteBuffer = ByteBuffer.allocate(byteLength).order(ByteOrder.nativeOrder());
+
+        if (klass == int[].class) {
+            byteBuffer.asIntBuffer().put((int[])array);
+        } else if (klass == float[].class) {
+            byteBuffer.asFloatBuffer().put((float[])array);
+        } else if (klass == double[].class) {
+            byteBuffer.asDoubleBuffer().put((double[])array);
+        } else if (klass == short[].class) {
+            byteBuffer.asShortBuffer().put((short[])array);
+        } else if (klass == char[].class) {
+            byteBuffer.asCharBuffer().put((char[])array);
+        } else if (klass == long[].class) {
+            byteBuffer.asLongBuffer().put((long[])array);
+        } else {
+            throw new IllegalArgumentException("array class invalid; must be a primitive array");
+        }
+
+        return byteBuffer.array();
+    }
+
+    private ByteArrayHelpers() {
+        throw new AssertionError();
+    }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index 45df065..b28733a 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -16,18 +16,30 @@
 
 package com.android.mediaframeworktest.unit;
 
-import android.os.Parcel;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Range;
+import android.util.SizeF;
+import android.graphics.ImageFormat;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.ColorSpaceTransform;
 import android.hardware.camera2.Face;
+import android.hardware.camera2.MeteringRectangle;
 import android.hardware.camera2.Rational;
+import android.hardware.camera2.ReprocessFormatsMap;
+import android.hardware.camera2.RggbChannelVector;
 import android.hardware.camera2.Size;
+import android.hardware.camera2.StreamConfiguration;
+import android.hardware.camera2.StreamConfigurationDuration;
 import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.marshal.impl.MarshalQueryableEnum;
+import android.hardware.camera2.utils.TypeReference;
 
 import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static com.android.mediaframeworktest.unit.ByteArrayHelpers.*;
 
 import java.lang.reflect.Array;
 import java.nio.ByteBuffer;
@@ -43,7 +55,6 @@
 public class CameraMetadataTest extends junit.framework.TestCase {
 
     CameraMetadataNative mMetadata;
-    Parcel mParcel;
 
     // Sections
     static final int ANDROID_COLOR_CORRECTION = 0;
@@ -64,15 +75,11 @@
     @Override
     public void setUp() {
         mMetadata = new CameraMetadataNative();
-        mParcel = Parcel.obtain();
     }
 
     @Override
     public void tearDown() throws Exception {
         mMetadata = null;
-
-        mParcel.recycle();
-        mParcel = null;
     }
 
     @SmallTest
@@ -130,10 +137,14 @@
 
     @SmallTest
     public void testGetTypeFromTag() {
-        assertEquals(TYPE_BYTE, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_MODE));
-        assertEquals(TYPE_RATIONAL, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM));
-        assertEquals(TYPE_FLOAT, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_GAINS));
-        assertEquals(TYPE_BYTE, CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE));
+        assertEquals(TYPE_BYTE,
+                CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_MODE));
+        assertEquals(TYPE_RATIONAL,
+                CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM));
+        assertEquals(TYPE_FLOAT,
+                CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_GAINS));
+        assertEquals(TYPE_BYTE,
+                CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE));
         assertEquals(TYPE_INT32,
                 CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION));
 
@@ -215,35 +226,163 @@
         assertEquals(1, mMetadata.getEntryCount());
     }
 
+    /**
+     * Format an array into a string with the {@code badIndex} highlighted with {@code **}.
+     *
+     * <p>Numbers are printed as hexadecimal values.</p>
+     *
+     * <p>Example: {@code "[hello, **world**]"} for a {@code string[]},
+     * or a {@code "[**0xFF**, 0xFF]"} for a {@code int[]}.</p>
+     */
+    private static <T> String formatArray(T array, int badIndex) {
+        StringBuilder builder = new StringBuilder();
+
+        builder.append("[");
+
+        int len = Array.getLength(array);
+        for (int i = 0; i < len; ++i) {
+
+            Object elem = Array.get(array, i);
+
+            if (i == badIndex) {
+                builder.append("**");
+            }
+
+            if (elem instanceof Number) {
+                builder.append(String.format("%x", elem));
+            } else {
+                builder.append(elem);
+            }
+
+            if (i == badIndex) {
+                builder.append("**");
+            }
+
+            if (i != len - 1) {
+                builder.append(", ");
+            }
+        }
+
+        builder.append("]");
+
+        return builder.toString();
+    }
+
     private static <T> void assertArrayEquals(T expected, T actual) {
-        assertEquals(Array.getLength(expected), Array.getLength(actual));
+        if (!expected.getClass().isArray() || !actual.getClass().isArray()) {
+            throw new IllegalArgumentException("expected, actual must both be arrays");
+        }
+
+        assertEquals("Array lengths must be equal",
+                Array.getLength(expected), Array.getLength(actual));
 
         int len = Array.getLength(expected);
         for (int i = 0; i < len; ++i) {
-            assertEquals(Array.get(expected, i), Array.get(actual, i));
+
+            Object expectedElement = Array.get(expected, i);
+            Object actualElement = Array.get(actual, i);
+
+            if (!expectedElement.equals(actualElement)) {
+                fail(String.format(
+                        "element %d in array was not equal (expected %s, actual %s). "
+                                + "Arrays were: (expected %s, actual %s).",
+                                i, expectedElement, actualElement,
+                                formatArray(expected, i),
+                                formatArray(actual, i)));
+            }
         }
     }
 
-    private <T> void checkKeyGetAndSet(String keyStr, Class<T> type, T value) {
-        assertFalse("Use checkKeyGetAndSetArray to compare array Keys", type.isArray());
-
-        Key<T> key = new Key<T>(keyStr, type);
+    private <T> void checkKeyGetAndSet(String keyStr, TypeReference<T> typeToken, T expected,
+            boolean reuse) {
+        Key<T> key = new Key<T>(keyStr, typeToken);
         assertNull(mMetadata.get(key));
         mMetadata.set(key, null);
         assertNull(mMetadata.get(key));
-        mMetadata.set(key, value);
+        mMetadata.set(key, expected);
 
         T actual = mMetadata.get(key);
-        assertEquals(value, actual);
+
+        if (typeToken.getRawType().isArray()) {
+            assertArrayEquals(expected, actual);
+        } else {
+            assertEquals(expected, actual);
+        }
+
+        if (reuse) {
+            // reset the key incase we want to use it again
+            mMetadata.set(key, null);
+        }
     }
 
-    private <T> void checkKeyGetAndSetArray(String keyStr, Class<T> type, T value) {
-        assertTrue(type.isArray());
+    private <T> void checkKeyGetAndSet(String keyStr, TypeReference<T> typeToken, T expected) {
+        checkKeyGetAndSet(keyStr, typeToken, expected, /*reuse*/false);
+    }
 
-        Key<T> key = new Key<T>(keyStr, type);
+    private <T> void checkKeyGetAndSet(String keyStr, Class<T> type, T expected) {
+        checkKeyGetAndSet(keyStr, TypeReference.createSpecializedTypeReference(type), expected);
+    }
+
+    /**
+     * Ensure that the data survives a marshal/unmarshal round-trip;
+     * it must also be equal to the {@code expectedNative} byte array.
+     *
+     * <p>As a side-effect, the metadata value corresponding to the key is now set to
+     * {@code expected}.</p>
+     *
+     * @return key created with {@code keyName} and {@code T}
+     */
+    private <T> Key<T> checkKeyMarshal(String keyName, TypeReference<T> typeReference,
+            T expected, byte[] expectedNative) {
+        Key<T> key = new Key<T>(keyName, typeReference);
+
+        mMetadata.set(key, null);
         assertNull(mMetadata.get(key));
-        mMetadata.set(key, value);
-        assertArrayEquals(value, mMetadata.get(key));
+
+        // Write managed value -> make sure native bytes are what we expect
+        mMetadata.set(key, expected);
+
+        byte[] actualValues = mMetadata.readValues(key.getTag());
+        assertArrayEquals(expectedNative, actualValues);
+
+        // Write managed value -> make sure read-out managed value is what we expect
+        T actual = mMetadata.get(key);
+
+        if (typeReference.getRawType().isArray()) {
+            assertArrayEquals(expected, actual);
+        } else {
+            assertEquals(expected, actual);
+        }
+
+        // Write native bytes -> make sure read-out managed value is what we expect
+        mMetadata.writeValues(key.getTag(), expectedNative);
+        actual = mMetadata.get(key);
+
+        if (typeReference.getRawType().isArray()) {
+            assertArrayEquals(expected, actual);
+        } else {
+            assertEquals(expected, actual);
+        }
+
+        return key;
+    }
+
+    /**
+     * Ensure that the data survives a marshal/unmarshal round-trip;
+     * it must also be equal to the {@code expectedNative} byte array.
+     *
+     * <p>As a side-effect,
+     * the metadata value corresponding to the key is now set to {@code expected}.</p>
+     *
+     * @return key created with {@code keyName} and {@code T}
+     */
+    private <T> Key<T> checkKeyMarshal(String keyName, T expected, byte[] expectedNative) {
+        @SuppressWarnings("unchecked")
+        Class<T> expectedClass = (Class<T>) expected.getClass();
+        return checkKeyMarshal(keyName,
+                TypeReference.createSpecializedTypeReference(expectedClass),
+                expected,
+                expectedNative);
     }
 
     @SmallTest
@@ -280,36 +419,36 @@
     @SmallTest
     public void testReadWritePrimitiveArray() {
         // int32 (n)
-        checkKeyGetAndSetArray("android.sensor.info.sensitivityRange", int[].class,
+        checkKeyGetAndSet("android.sensor.info.sensitivityRange", int[].class,
                 new int[] {
                         0xC0FFEE, 0xDEADF00D
                 });
 
         // byte (n)
-        checkKeyGetAndSetArray("android.statistics.faceScores", byte[].class, new byte[] {
+        checkKeyGetAndSet("android.statistics.faceScores", byte[].class, new byte[] {
                 1, 2, 3, 4
         });
 
         // int64 (n)
-        checkKeyGetAndSetArray("android.scaler.availableProcessedMinDurations", long[].class,
+        checkKeyGetAndSet("android.scaler.availableProcessedMinDurations", long[].class,
                 new long[] {
                         0xABCD12345678FFFFL, 0x1234ABCD5678FFFFL, 0xFFFF12345678ABCDL
                 });
 
         // float (n)
-        checkKeyGetAndSetArray("android.lens.info.availableApertures", float[].class,
+        checkKeyGetAndSet("android.lens.info.availableApertures", float[].class,
                 new float[] {
                         Float.MAX_VALUE, Float.MIN_NORMAL, Float.MIN_VALUE
                 });
 
         // double (n) -- in particular double x 3
-        checkKeyGetAndSetArray("android.jpeg.gpsCoordinates", double[].class,
+        checkKeyGetAndSet("android.jpeg.gpsCoordinates", double[].class,
                 new double[] {
                         Double.MAX_VALUE, Double.MIN_NORMAL, Double.MIN_VALUE
                 });
 
         // rational (n) -- in particular rational x 9
-        checkKeyGetAndSetArray("android.sensor.calibrationTransform1", Rational[].class,
+        checkKeyGetAndSet("android.sensor.calibrationTransform1", Rational[].class,
                 new Rational[] {
                         new Rational(1, 2), new Rational(3, 4), new Rational(5, 6),
                         new Rational(7, 8), new Rational(9, 10), new Rational(10, 11),
@@ -321,13 +460,12 @@
          */
 
         // bool (n) -- with TYPE_BYTE
-        checkKeyGetAndSetArray("android.control.aeLock", boolean[].class, new boolean[] {
+        checkKeyGetAndSet("android.control.aeLock", boolean[].class, new boolean[] {
                 true, false, true
         });
 
-
         // integer (n) -- with TYPE_BYTE
-        checkKeyGetAndSetArray("android.control.aeAvailableModes", int[].class, new int[] {
+        checkKeyGetAndSet("android.control.aeAvailableModes", int[].class, new int[] {
             1, 2, 3, 4
         });
     }
@@ -345,7 +483,6 @@
         AUTO
     }
 
-    // TODO: special values for the enum.
     private enum AvailableFormat {
         RAW_SENSOR,
         YV12,
@@ -366,7 +503,7 @@
                 AeAntibandingMode.AUTO);
 
         // byte (n)
-        checkKeyGetAndSetArray("android.control.aeAvailableAntibandingModes",
+        checkKeyGetAndSet("android.control.aeAvailableAntibandingModes",
                 AeAntibandingMode[].class, new AeAntibandingMode[] {
                         AeAntibandingMode.OFF, AeAntibandingMode._50HZ, AeAntibandingMode._60HZ,
                         AeAntibandingMode.AUTO
@@ -376,7 +513,7 @@
          * Stranger cases that don't use byte enums
          */
         // int (n)
-        checkKeyGetAndSetArray("android.scaler.availableFormats", AvailableFormat[].class,
+        checkKeyGetAndSet("android.scaler.availableFormats", AvailableFormat[].class,
                 new AvailableFormat[] {
                         AvailableFormat.RAW_SENSOR,
                         AvailableFormat.YV12,
@@ -389,7 +526,7 @@
 
     @SmallTest
     public void testReadWriteEnumWithCustomValues() {
-        CameraMetadataNative.registerEnumValues(AeAntibandingMode.class, new int[] {
+        MarshalQueryableEnum.registerEnumValues(AeAntibandingMode.class, new int[] {
             0,
             10,
             20,
@@ -401,15 +538,12 @@
                 AeAntibandingMode.AUTO);
 
         // byte (n)
-        checkKeyGetAndSetArray("android.control.aeAvailableAntibandingModes",
+        checkKeyGetAndSet("android.control.aeAvailableAntibandingModes",
                 AeAntibandingMode[].class, new AeAntibandingMode[] {
                         AeAntibandingMode.OFF, AeAntibandingMode._50HZ, AeAntibandingMode._60HZ,
                         AeAntibandingMode.AUTO
                 });
 
-        Key<AeAntibandingMode[]> aeAntibandingModeKey =
-                new Key<AeAntibandingMode[]>("android.control.aeAvailableAntibandingModes",
-                        AeAntibandingMode[].class);
         byte[] aeAntibandingModeValues = mMetadata.readValues(CameraMetadataNative
                 .getTag("android.control.aeAvailableAntibandingModes"));
         byte[] expectedValues = new byte[] { 0, 10, 20, 30 };
@@ -420,7 +554,7 @@
          * Stranger cases that don't use byte enums
          */
         // int (n)
-        CameraMetadataNative.registerEnumValues(AvailableFormat.class, new int[] {
+        MarshalQueryableEnum.registerEnumValues(AvailableFormat.class, new int[] {
             0x20,
             0x32315659,
             0x11,
@@ -429,7 +563,7 @@
             0x21,
         });
 
-        checkKeyGetAndSetArray("android.scaler.availableFormats", AvailableFormat[].class,
+        checkKeyGetAndSet("android.scaler.availableFormats", AvailableFormat[].class,
                 new AvailableFormat[] {
                         AvailableFormat.RAW_SENSOR,
                         AvailableFormat.YV12,
@@ -466,7 +600,7 @@
         checkKeyGetAndSet("android.jpeg.thumbnailSize", Size.class, new Size(123, 456));
 
         // int32 x 2 x n
-        checkKeyGetAndSetArray("android.scaler.availableJpegSizes", Size[].class, new Size[] {
+        checkKeyGetAndSet("android.scaler.availableJpegSizes", Size[].class, new Size[] {
             new Size(123, 456),
             new Size(0xDEAD, 0xF00D),
             new Size(0xF00, 0xB00)
@@ -474,16 +608,217 @@
     }
 
     @SmallTest
-    public void testReadWriteRectangle() {
+    public void testReadWriteRggbChannelVector() {
         // int32 x n
-        checkKeyGetAndSet("android.scaler.cropRegion", Rect.class, new Rect(10, 11, 1280, 1024));
+        checkKeyMarshal("android.colorCorrection.gains",
+                new RggbChannelVector(1.0f, 2.1f, 3.2f, 4.5f),
+                toByteArray(1.0f, 2.1f, 3.2f, 4.5f));
+
+        // int32 x 2 x n [pretend; actual is not array]
+        checkKeyMarshal("android.colorCorrection.gains",
+                new RggbChannelVector[] {
+                    new RggbChannelVector(1.0f, 2.0f, 3.0f, 4.0f),
+                    new RggbChannelVector(9.0f, 8.0f, 7.0f, 6.0f),
+                    new RggbChannelVector(1.3f, 5.5f, 2.4f, 6.7f),
+                }, toByteArray(
+                        1.0f, 2.0f, 3.0f, 4.0f,
+                        9.0f, 8.0f, 7.0f, 6.0f,
+                        1.3f, 5.5f, 2.4f, 6.7f
+                ));
+    }
+
+    @SmallTest
+    public void testReadWriteSizeF() {
+        // int32 x n
+        checkKeyMarshal("android.sensor.info.physicalSize",
+                new SizeF(123f, 456f),
+                toByteArray(123f, 456f));
 
         // int32 x 2 x n
-        checkKeyGetAndSetArray("android.statistics.faceRectangles", Rect[].class, new Rect[] {
+        checkKeyMarshal("android.sensor.info.physicalSize",
+                new SizeF[] {
+                    new SizeF(123f, 456f),
+                    new SizeF(1.234f, 4.567f),
+                    new SizeF(999.0f, 555.0f)
+                },
+                toByteArray(
+                        123f, 456f,
+                        1.234f, 4.567f,
+                        999.0f, 555.0f)
+        );
+    }
+
+    @SmallTest
+    public void testReadWriteRectangle() {
+        // int32 x n
+        checkKeyMarshal("android.scaler.cropRegion",
+                // x1, y1, x2, y2
+                new Rect(10, 11, 1280, 1024),
+                // x, y, width, height
+                toByteArray(10, 11, 1280 - 10, 1024 - 11));
+
+        // int32 x 2 x n  [actually not array, but we pretend it is]
+        checkKeyMarshal("android.scaler.cropRegion", new Rect[] {
             new Rect(110, 111, 11280, 11024),
             new Rect(210, 111, 21280, 21024),
             new Rect(310, 111, 31280, 31024)
-        });
+        }, toByteArray(
+                110, 111, 11280 - 110, 11024 - 111,
+                210, 111, 21280 - 210, 21024 - 111,
+                310, 111, 31280 - 310, 31024 - 111
+        ));
+    }
+
+    @SmallTest
+    public void testReadWriteMeteringRectangle() {
+        // int32 x 5 x area_count [but we pretend it's a single element]
+        checkKeyMarshal("android.control.aeRegions",
+                new MeteringRectangle(/*x*/1, /*y*/2, /*width*/100, /*height*/200, /*weight*/5),
+                /* xmin, ymin, xmax, ymax, weight */
+                toByteArray(1, 2, 1 + 100, 2 + 200, 5));
+
+        // int32 x 5 x area_count
+        checkKeyMarshal("android.control.afRegions",
+                new MeteringRectangle[] {
+                    new MeteringRectangle(/*x*/5, /*y*/6, /*width*/123, /*height*/456, /*weight*/7),
+                    new MeteringRectangle(/*x*/7, /*y*/8, /*width*/456, /*height*/999, /*weight*/6),
+                    new MeteringRectangle(/*x*/1, /*y*/2, /*width*/100, /*height*/200, /*weight*/5)
+                },
+                toByteArray(
+                        5, 6, 5 + 123, 6 + 456, 7,
+                        7, 8, 7 + 456, 8 + 999, 6,
+                        1, 2, 1 + 100, 2 + 200, 5
+        ));
+    }
+
+    @SmallTest
+    public void testReadWriteColorSpaceTransform() {
+        // rational x 3 x 3
+        checkKeyMarshal("android.colorCorrection.transform",
+                new ColorSpaceTransform(new Rational[] {
+                        new Rational(1, 2), new Rational(3, 4), new Rational(5, 6),
+                        new Rational(7, 8), new Rational(8, 9), new Rational(10, 11),
+                        new Rational(1, 5), new Rational(2, 8), new Rational(3, 9),
+                }),
+                toByteArray(
+                        1, 2, 3, 4, 5, 6,
+                        7, 8, 8, 9, 10, 11,
+                        1, 5, 2, 8, 3, 9));
+    }
+
+    @SmallTest
+    public void testReadWritePoint() {
+        // int32 x 2 [actually 'x n' but pretend it's a single value for now]
+        checkKeyMarshal("android.statistics.hotPixelMap",
+                new Point(1, 2),
+                toByteArray(1, 2));
+
+        // int32 x 2 x samples
+        checkKeyMarshal("android.statistics.hotPixelMap",
+                new Point[] {
+                    new Point(1, 2),
+                    new Point(3, 4),
+                    new Point(5, 6),
+                    new Point(7, 8),
+                },
+                toByteArray(
+                        1, 2,
+                        3, 4,
+                        5, 6,
+                        7, 8)
+        );
+    }
+
+    @SmallTest
+    public void testReadWritePointF() {
+        // float x 2 [actually 'x samples' but pretend it's a single value for now]
+        checkKeyMarshal(
+                "android.sensor.profileToneCurve",
+                new PointF(1.0f, 2.0f),
+                toByteArray(1.0f, 2.0f));
+
+        // float x 2 x samples
+        checkKeyMarshal("android.sensor.profileToneCurve",
+                new PointF[] {
+                    new PointF(1.0f, 2.0f),
+                    new PointF(3.0f, 4.0f),
+                    new PointF(5.0f, 6.0f),
+                    new PointF(7.0f, 8.0f),
+                },
+                toByteArray(
+                        1.0f, 2.0f,
+                        3.0f, 4.0f,
+                        5.0f, 6.0f,
+                        7.0f, 8.0f));
+    }
+
+    @SmallTest
+    public void testReadWriteRange() {
+        // int32 x 2
+        checkKeyMarshal("android.control.aeTargetFpsRange",
+                new TypeReference<Range<Integer>>() {{ }},
+                Range.create(123, 456),
+                toByteArray(123, 456));
+
+        // int64 x 2
+        checkKeyMarshal("android.sensor.info.exposureTimeRange",
+                new TypeReference<Range<Long>>() {{ }},
+                Range.create(123L, 456L),
+                toByteArray(123L, 456L));
+    }
+
+    @SmallTest
+    public void testReadWriteStreamConfiguration() {
+        // int32 x 4 x n
+        checkKeyMarshal("android.scaler.availableStreamConfigurations",
+                new StreamConfiguration[] {
+                    new StreamConfiguration(ImageFormat.YUV_420_888, 640, 480, /*input*/false),
+                    new StreamConfiguration(ImageFormat.RGB_565, 320, 240, /*input*/true),
+                },
+                toByteArray(
+                        ImageFormat.YUV_420_888, 640, 480, /*input*/0,
+                        ImageFormat.RGB_565, 320, 240, /*input*/1)
+        );
+    }
+
+    @SmallTest
+    public void testReadWriteStreamConfigurationDuration() {
+        // Avoid sign extending ints when converting to a long
+        final long MASK_UNSIGNED_INT = 0x00000000ffffffffL;
+
+        // int64 x 4 x n
+        checkKeyMarshal("android.scaler.availableMinFrameDurations",
+                new StreamConfigurationDuration[] {
+                    new StreamConfigurationDuration(
+                            ImageFormat.YUV_420_888, 640, 480, /*duration*/123L),
+                    new StreamConfigurationDuration(
+                            ImageFormat.RGB_565, 320, 240, /*duration*/345L),
+                },
+                toByteArray(
+                        ImageFormat.YUV_420_888 & MASK_UNSIGNED_INT, 640L, 480L, /*duration*/123L,
+                        ImageFormat.RGB_565 & MASK_UNSIGNED_INT, 320L, 240L, /*duration*/345L)
+        );
+    }
+
+
+    @SmallTest
+    public void testReadWriteReprocessFormatsMap() {
+
+        final int RAW_OPAQUE = 0x24;
+        final int RAW16 = ImageFormat.RAW_SENSOR;
+        final int YUV_420_888 = ImageFormat.YUV_420_888;
+        final int BLOB = 0x21;
+
+        int[] contents = new int[] {
+                RAW_OPAQUE, 3, RAW16, YUV_420_888, BLOB,
+                RAW16, 2, YUV_420_888, BLOB,
+        };
+
+        // int32 x n
+        checkKeyMarshal("android.scaler.availableInputOutputFormatsMap",
+                new ReprocessFormatsMap(contents),
+                toByteArray(contents)
+        );
     }
 
     @SmallTest
@@ -525,10 +860,6 @@
         assertArrayEquals(gpsBytes, actualBytes2);
     }
 
-    <T> void compareGeneric(T expected, T actual) {
-        assertEquals(expected, actual);
-    }
-
     @SmallTest
     public void testReadWriteOverride() {
         //
@@ -691,7 +1022,7 @@
      */
     private <T> void validateArrayMetadataReadWriteOverride(Key<T> key, T writeValues,
             T readValues, int tag) {
-        Class<T> type = key.getType();
+        Class<?> type = writeValues.getClass();
         if (!type.isArray()) {
             throw new IllegalArgumentException("This function expects an key with array type");
         } else if (type != int[].class && type != long[].class) {
@@ -737,4 +1068,20 @@
         assertNotNull(key.getName() + " result shouldn't be null", result);
         assertArrayEquals(writeValues, result);
     }
+
+    // TODO: move somewhere else
+    @SmallTest
+    public void testToByteArray() {
+        assertArrayEquals(new byte[] { 5, 0, 0, 0, 6, 0, 0, 0 },
+                toByteArray(5, 6));
+        assertArrayEquals(new byte[] { 5, 0, 6, 0, },
+                toByteArray((short)5, (short)6));
+        assertArrayEquals(new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
+                                        (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,},
+                toByteArray(~0, ~0));
+
+        assertArrayEquals(new byte[] { (byte)0xAB, (byte)0xFF, 0, 0,
+                0x0D, (byte)0xF0, (byte)0xAD, (byte)0xDE },
+                toByteArray(0xFFAB, 0xDEADF00D));
+    }
 }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsTypeReferenceTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsTypeReferenceTest.java
new file mode 100644
index 0000000..1b765ea
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsTypeReferenceTest.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.unit;
+
+import static android.hardware.camera2.utils.TypeReference.*;
+
+import android.hardware.camera2.utils.TypeReference;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import java.lang.reflect.Type;
+import java.util.List;
+
+public class CameraUtilsTypeReferenceTest extends junit.framework.TestCase {
+    private static final String TAG = CameraUtilsTypeReferenceTest.class.getSimpleName();
+    private static final boolean VERBOSE = false;
+
+    private class RegularClass {}
+    private class SubClass extends RegularClass {}
+
+    private class GenericClass<T> {}
+    private class SubGenericClass<T> extends GenericClass<T> {}
+
+    private class SpecificClass extends GenericClass<Integer> {}
+
+    private interface RegularInterface {}
+    private interface GenericInterface<T> {}
+    private interface GenericInterface2<T> {}
+
+    private class ImplementsRegularInterface implements RegularInterface {}
+    private class ImplementsGenericInterface<T> implements GenericInterface<T> {}
+    private class Implements2GenericInterface<T>
+        implements GenericInterface<Integer>, GenericInterface2<T> {}
+
+    private class GenericOuterClass<T> {
+        class GenericInnerClass {
+            @SuppressWarnings("unused")
+            T field;
+        }
+    }
+
+    private static void assertContainsTypeVariable(Type type) {
+        assertTrue(type.toString() + " was expected to have a type variable, but it didn't",
+                containsTypeVariable(type));
+    }
+
+    private static void assertLacksTypeVariable(Type type) {
+        assertFalse(type.toString() + " was expected to *not* have a type variable, but it did",
+                containsTypeVariable(type));
+    }
+
+    /*
+     * Only test classes and interfaces. Other types are not tested (e.g. fields, methods, etc).
+     */
+
+    @SmallTest
+    public void testLacksTypeVariables() {
+        assertLacksTypeVariable(RegularClass.class);
+        assertLacksTypeVariable(SubClass.class);
+        assertLacksTypeVariable(SpecificClass.class);
+
+        assertLacksTypeVariable(RegularInterface.class);
+        assertLacksTypeVariable(ImplementsRegularInterface.class);
+    }
+
+    @SmallTest
+    public void testContainsTypeVariables() {
+        assertContainsTypeVariable(GenericClass.class);
+        assertContainsTypeVariable(SubGenericClass.class);
+
+        assertContainsTypeVariable(GenericInterface.class);
+        assertContainsTypeVariable(ImplementsGenericInterface.class);
+        assertContainsTypeVariable(Implements2GenericInterface.class);
+
+        assertContainsTypeVariable(GenericOuterClass.class);
+        assertContainsTypeVariable(GenericOuterClass.GenericInnerClass.class);
+    }
+
+    /**
+     * This should always throw an IllegalArgumentException since the
+     * type reference to {@code T} will contain a type variable (also {@code T}).
+     *
+     * @throws IllegalArgumentException unconditionally
+     */
+    private static <T> TypeReference<T> createTypeRefWithTypeVar() {
+        return new TypeReference<T>() {{ }};
+    }
+
+    @SmallTest
+    public void testTypeReferences() {
+        TypeReference<Integer> typeRefInt = new TypeReference<Integer>() {{ }};
+        TypeReference<Integer> typeRefInt2 = new TypeReference<Integer>() {{ }};
+
+        assertEquals(typeRefInt, typeRefInt2);
+        assertEquals("The type ref's captured type should be the Integer class",
+                Integer.class, typeRefInt.getType());
+
+        TypeReference<Float> typeRefFloat = new TypeReference<Float>() {{ }};
+        assertFalse("Integer/Float type references must not be equal",
+                typeRefInt.equals(typeRefFloat));
+        assertEquals("The type ref's captured type should be the Float class",
+                Float.class, typeRefFloat.getType());
+
+        try {
+            TypeReference<Integer> typeRefTypeVar = createTypeRefWithTypeVar();
+            fail("Expected a type reference with type variables to fail");
+            // Unreachable. Make the warning about an unused variable go away.
+            assertFalse(typeRefTypeVar.equals(typeRefInt));
+        } catch (IllegalArgumentException e) {
+            // OK. Expected behavior
+        }
+    }
+
+    // Compare the raw type against rawClass
+    private static <T> void assertRawTypeEquals(TypeReference<T> typeRef, Class<?> rawClass) {
+        assertEquals("Expected the raw type from " + typeRef + " to match the class " + rawClass,
+                rawClass, typeRef.getRawType());
+    }
+
+    // Compare the normal type against the klass
+    private static <T> void assertTypeReferenceEquals(TypeReference<T> typeRef, Class<?> klass) {
+        assertEquals("Expected the type from " + typeRef + " to match the class " + klass,
+                klass, typeRef.getType());
+    }
+
+    @SmallTest
+    public void testRawTypes() {
+        TypeReference<Integer> intToken = new TypeReference<Integer>() {{ }};
+        assertRawTypeEquals(intToken, Integer.class);
+
+        TypeReference<List<Integer>> listToken = new TypeReference<List<Integer>>() {{ }};
+        assertRawTypeEquals(listToken, List.class);
+
+        TypeReference<List<List<Integer>>> listListToken =
+                new TypeReference<List<List<Integer>>>() {{ }};
+        assertRawTypeEquals(listListToken, List.class);
+
+        TypeReference<int[]> intArrayToken = new TypeReference<int[]>() {{ }};
+        assertRawTypeEquals(intArrayToken, int[].class);
+
+        TypeReference<Integer[]> integerArrayToken = new TypeReference<Integer[]>() {{ }};
+        assertRawTypeEquals(integerArrayToken, Integer[].class);
+
+        TypeReference<List<Integer>[]> listArrayToken = new TypeReference<List<Integer>[]>() {{ }};
+        assertRawTypeEquals(listArrayToken, List[].class);
+    }
+
+    private class IntTokenOne extends TypeReference<Integer> {}
+    private class IntTokenTwo extends TypeReference<Integer> {}
+
+    private class IntArrayToken1 extends TypeReference<Integer[]> {}
+    private class IntArrayToken2 extends TypeReference<Integer[]> {}
+
+    private class IntListToken1 extends TypeReference<List<Integer>> {}
+    private class IntListToken2 extends TypeReference<List<Integer>> {}
+
+    private class IntListArrayToken1 extends TypeReference<List<Integer>[]> {}
+    private class IntListArrayToken2 extends TypeReference<List<Integer>[]> {}
+
+
+    // FIXME: Equality will fail: b/14590652
+    @SmallTest
+    public void testEquals() {
+        // Not an array. component type should be null.
+        TypeReference<Integer> intToken = new TypeReference<Integer>() {{ }};
+        assertEquals(intToken, intToken);
+        assertEquals(intToken, new TypeReference<Integer>() {{ }});
+
+        assertEquals(intToken, new IntTokenOne());
+        assertEquals(intToken, new IntTokenTwo());
+        assertEquals(new IntTokenOne(), new IntTokenTwo());
+
+        assertEquals(new IntArrayToken1(), new IntArrayToken2());
+        assertEquals(new IntListToken1(), new IntListToken2());
+        assertEquals(new IntListArrayToken1(), new IntListArrayToken2());
+    }
+
+    @SmallTest
+    public void testComponentType() {
+        // Not an array. component type should be null.
+        TypeReference<Integer> intToken = new TypeReference<Integer>() {{ }};
+        assertNull(intToken.getComponentType());
+
+        TypeReference<List<Integer>> listToken = new TypeReference<List<Integer>>() {{ }};
+        assertNull(listToken.getComponentType());
+
+        TypeReference<List<List<Integer>>> listListToken =
+                new TypeReference<List<List<Integer>>>() {{ }};
+        assertNull(listListToken.getComponentType());
+
+        // Check arrays. Component types should be what we expect.
+        TypeReference<int[]> intArrayToken = new TypeReference<int[]>() {{ }};
+        assertTypeReferenceEquals(intArrayToken.getComponentType(), int.class);
+
+        TypeReference<Integer[]> integerArrayToken = new TypeReference<Integer[]>() {{ }};
+        assertTypeReferenceEquals(integerArrayToken.getComponentType(), Integer.class);
+
+        assertEquals(new IntArrayToken1().getComponentType(),
+                new IntArrayToken2().getComponentType());
+
+        assertEquals(new IntListArrayToken1().getComponentType(),
+                new IntListArrayToken2().getComponentType());
+
+        // FIXME: Equality will fail: b/14590652
+        TypeReference<List<Integer>[]> listArrayToken = new TypeReference<List<Integer>[]>() {{ }};
+        assertEquals(listToken, listArrayToken.getComponentType());
+    }
+}
diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml
index e8944ec..8c9030d 100644
--- a/packages/DocumentsUI/res/values-km-rKH/strings.xml
+++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml
@@ -20,14 +20,14 @@
     <string name="title_open" msgid="4353228937663917801">"បើក​ពី"</string>
     <string name="title_save" msgid="2433679664882857999">"រក្សា​ទុក​ទៅ"</string>
     <string name="menu_create_dir" msgid="5947289605844398389">"បង្កើត​ថត"</string>
-    <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាព​ក្រឡា"</string>
+    <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាព​ក្រឡា​"</string>
     <string name="menu_list" msgid="7279285939892417279">"ទិដ្ឋភាព​បញ្ជី"</string>
     <string name="menu_sort" msgid="7677740407158414452">"តម្រៀប​តាម"</string>
     <string name="menu_search" msgid="3816712084502856974">"ស្វែងរក"</string>
     <string name="menu_settings" msgid="6008033148948428823">"ការ​កំណត់"</string>
     <string name="menu_open" msgid="432922957274920903">"បើក"</string>
     <string name="menu_save" msgid="2394743337684426338">"រក្សាទុក"</string>
-    <string name="menu_share" msgid="3075149983979628146">"ចែករំលែក"</string>
+    <string name="menu_share" msgid="3075149983979628146">"ចែករំលែក​"</string>
     <string name="menu_delete" msgid="8138799623850614177">"លុប"</string>
     <string name="menu_select" msgid="8711270657353563424">"ជ្រើស \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
     <string name="mode_selected_count" msgid="459111894725594625">"បាន​ជ្រើស <xliff:g id="COUNT">%1$d</xliff:g>"</string>
@@ -48,7 +48,7 @@
     <string name="pref_advanced_devices" msgid="903257239609301276">"បង្ហាញ​ឧបករណ៍​កម្រិត​ខ្ពស់"</string>
     <string name="pref_file_size" msgid="2826879315743961459">"បង្ហាញ​ទំហំ​ឯកសារ"</string>
     <string name="pref_device_size" msgid="3542106883278997222">"បង្ហាញ​ទំហំ​ឧបករណ៍"</string>
-    <string name="empty" msgid="7858882803708117596">"គ្មានធាតុ"</string>
+    <string name="empty" msgid="7858882803708117596">"គ្មានធាតុ​"</string>
     <string name="toast_no_application" msgid="1339885974067891667">"មិន​អាច​បើក​ឯកសារ"</string>
     <string name="toast_failed_delete" msgid="2180678019407244069">"មិន​អាច​លុប​ឯកសារ​មួយ​ចំនួន"</string>
     <string name="share_via" msgid="8966594246261344259">"ចែករំលែក​តាម"</string>
diff --git a/packages/Keyguard/res/values-km-rKH/strings.xml b/packages/Keyguard/res/values-km-rKH/strings.xml
index a2e54a7..18b59f1 100644
--- a/packages/Keyguard/res/values-km-rKH/strings.xml
+++ b/packages/Keyguard/res/values-km-rKH/strings.xml
@@ -83,7 +83,7 @@
     <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
     <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
-    <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់"</string>
+    <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់​"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string>
     <string name="keyboardview_keycode_done" msgid="1992571118466679775">"រួចរាល់"</string>
     <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ប្ដូរ​របៀប"</string>
@@ -120,7 +120,7 @@
     <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ព្យាយាម​លំនាំ​ច្រើន​ពេក"</string>
     <string name="kg_login_instructions" msgid="1100551261265506448">"ដើម្បី​ដោះ​សោ ចូល​ក្នុង​គណនី Google ។"</string>
     <string name="kg_login_username_hint" msgid="5718534272070920364">"ឈ្មោះ​អ្នក​ប្រើ (អ៊ី​ម៉ែ​ល​)"</string>
-    <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
+    <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់​"</string>
     <string name="kg_login_submit_button" msgid="5355904582674054702">"ចូល"</string>
     <string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​មិន​ត្រឹមត្រូវ។"</string>
     <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លេច​ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​របស់​អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index 4b386b6..2d17b7b 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -68,13 +68,6 @@
         mEnableHaptics = mLockPatternUtils.isTactileFeedbackEnabled();
     }
 
-    @Override
-    public void onWindowFocusChanged(boolean hasWindowFocus) {
-        if (hasWindowFocus) {
-            reset();
-        }
-    }
-
     public void reset() {
         // start fresh
         mPasswordEntry.setText("");
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index b4308c6..98122fc 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -231,15 +231,6 @@
         mLockPatternView.setOnPatternListener(null);
     }
 
-    @Override
-    public void onWindowFocusChanged(boolean hasWindowFocus) {
-        super.onWindowFocusChanged(hasWindowFocus);
-        if (hasWindowFocus) {
-            // when timeout dialog closes we want to update our state
-            reset();
-        }
-    }
-
     private class UnlockPatternListener implements LockPatternView.OnPatternListener {
 
         public void onPatternStart() {
diff --git a/packages/PrintSpooler/res/values-km-rKH/strings.xml b/packages/PrintSpooler/res/values-km-rKH/strings.xml
index c89f9bf..ba3c042 100644
--- a/packages/PrintSpooler/res/values-km-rKH/strings.xml
+++ b/packages/PrintSpooler/res/values-km-rKH/strings.xml
@@ -60,7 +60,7 @@
   </plurals>
     <string name="cancel" msgid="4373674107267141885">"បោះបង់"</string>
     <string name="restart" msgid="2472034227037808749">"ចាប់ផ្ដើម​ឡើងវិញ"</string>
-    <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មាន​​​ការ​ភ្ជាប់​ទៅ​ម៉ាស៊ីន​បោះពុម្ព"</string>
+    <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មាន​​​ការ​ភ្ជាប់​ទៅ​ម៉ាស៊ីន​បោះពុម្ព​"</string>
     <string name="reason_unknown" msgid="5507940196503246139">"មិន​ស្គាល់"</string>
     <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – មិន​អាច​ប្រើ​បាន"</string>
     <string name="print_error_default_message" msgid="8568506918983980567">"មិន​អាច​បង្កើត​ការ​ងារ​បោះពុម្ព"</string>
diff --git a/packages/SystemUI/res/drawable/ic_qs_airplane.xml b/packages/SystemUI/res/drawable/ic_qs_airplane.xml
new file mode 100644
index 0000000..ffe571f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_airplane.xml
@@ -0,0 +1,31 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FF000000"
+        android:pathData="M10.2,9.0"/>
+    <path
+        android:fill="#FF000000"
+        android:pathData="M21.0,16.0l0.0,-2.0l-8.0,-5.0L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5L10.0,9.0l-8.0,5.0l0.0,2.0l8.0,-2.5L10.0,19.0l-2.0,1.5L8.0,22.0l3.5,-1.0l3.5,1.0l0.0,-1.5L13.0,19.0l0.0,-5.5L21.0,16.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth.xml
new file mode 100644
index 0000000..22d0dcf
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FF000000"
+        android:pathData="M17.7,7.7L12.0,2.0l-1.0,0.0l0.0,7.6L6.4,5.0L5.0,6.4l5.6,5.6L5.0,17.6L6.4,19.0l4.6,-4.6L11.0,22.0l1.0,0.0l5.7,-5.7L13.4,12.0L17.7,7.7zM13.0,5.8l1.9,1.9L13.0,9.6L13.0,5.8zM14.9,16.3L13.0,18.2l0.0,-3.8L14.9,16.3z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_bugreport.xml b/packages/SystemUI/res/drawable/ic_qs_bugreport.xml
new file mode 100644
index 0000000..5057390
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_bugreport.xml
@@ -0,0 +1,31 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FF000000"
+        android:pathData="M19.0,10.0c0.0,-2.3 -1.15,-4.35 -2.9,-5.65l2.101,-2.1L17.4,1.4L15.1,3.7C14.15,3.3 13.1,3.0 12.0,3.0S9.85,3.3 8.9,3.75L6.6,1.45L5.8,2.3L7.9,4.4C6.15,5.65 5.0,7.7 5.0,10.0l0.0,1.0l14.0,0.0L19.0,10.0zM9.0,9.0C8.45,9.0 8.0,8.55 8.0,8.0s0.45,-1.0 1.0,-1.0s1.0,0.45 1.0,1.0S9.55,9.0 9.0,9.0zM15.0,9.0c-0.55,0.0 -1.0,-0.45 -1.0,-1.0s0.45,-1.0 1.0,-1.0s1.0,0.45 1.0,1.0S15.55,9.0 15.0,9.0z"/>
+    <path
+        android:fill="#FF000000"
+        android:pathData="M18.984,16.299C18.989,16.199 19.0,16.102 19.0,16.0l0.0,-2.301l2.517,2.268l0.837,-0.929L19.0,12.018L19.0,12.0L5.0,12.0l0.0,0.021l-0.013,-0.014l-3.364,3.031l0.836,0.929L5.0,13.678L5.0,16.0c0.0,0.11 0.012,0.218 0.017,0.327l-0.029,-0.032l-3.364,3.031l0.836,0.929l2.774,-2.499C6.019,20.762 8.757,23.0 12.0,23.0c3.236,0.0 5.971,-2.229 6.762,-5.227l2.755,2.481l0.837,-0.929l-3.365,-3.031L18.984,16.299z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_cast.xml b/packages/SystemUI/res/drawable/ic_qs_cast.xml
new file mode 100644
index 0000000..6f2840b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_cast.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FF000000"
+        android:pathData="M21.0,3.0L3.0,3.0C1.9,3.0 1.0,3.9 1.0,5.0l0.0,3.0l2.0,0.0L3.0,5.0l18.0,0.0l0.0,14.0l-7.0,0.0l0.0,2.0l7.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L23.0,5.0C23.0,3.9 22.1,3.0 21.0,3.0zM1.0,18.0l0.0,3.0l3.0,0.0C4.0,19.3 2.7,18.0 1.0,18.0zM1.0,14.0l0.0,2.0c2.8,0.0 5.0,2.2 5.0,5.0l2.0,0.0C8.0,17.1 4.9,14.0 1.0,14.0zM1.0,10.0l0.0,2.0c5.0,0.0 9.0,4.0 9.0,9.0l2.0,0.0C12.0,14.9 7.1,10.0 1.0,10.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_close.xml b/packages/SystemUI/res/drawable/ic_qs_close.xml
new file mode 100644
index 0000000..c2c72c8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_close.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FF000000"
+        android:pathData="M19.0,6.4l-1.3999996,-1.4000001 -5.6000004,5.6000004 -5.6,-5.6000004 -1.4000001,1.4000001 5.6000004,5.6 -5.6000004,5.6000004 1.4000001,1.3999996 5.6,-5.6000004 5.6000004,5.6000004 1.3999996,-1.3999996 -5.6000004,-5.6000004z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_hotspot.xml b/packages/SystemUI/res/drawable/ic_qs_hotspot.xml
new file mode 100644
index 0000000..965e3c1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_hotspot.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FF000000"
+        android:pathData="M12.0,11.0c-1.1,0.0 -2.0,0.9 -2.0,2.0c0.0,1.1 0.9,2.0 2.0,2.0c1.1,0.0 2.0,-0.9 2.0,-2.0C14.0,11.9 13.1,11.0 12.0,11.0zM18.0,13.0c0.0,-3.3 -2.7,-6.0 -6.0,-6.0c-3.3,0.0 -6.0,2.7 -6.0,6.0c0.0,2.2 1.2,4.1 3.0,5.2l1.0,-1.7c-1.2,-0.7 -2.0,-2.0 -2.0,-3.4c0.0,-2.2 1.8,-4.0 4.0,-4.0s4.0,1.8 4.0,4.0c0.0,1.5 -0.8,2.8 -2.0,3.4l1.0,1.7C16.8,17.1 18.0,15.2 18.0,13.0zM12.0,3.0C6.5,3.0 2.0,7.5 2.0,13.0c0.0,3.7 2.0,6.9 5.0,8.6l1.0,-1.7c-2.4,-1.4 -4.0,-4.0 -4.0,-6.9c0.0,-4.4 3.6,-8.0 8.0,-8.0s8.0,3.6 8.0,8.0c0.0,3.0 -1.6,5.5 -4.0,6.9l1.0,1.7c3.0,-1.7 5.0,-5.0 5.0,-8.6C22.0,7.5 17.5,3.0 12.0,3.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_invert_colors.xml b/packages/SystemUI/res/drawable/ic_qs_invert_colors.xml
new file mode 100644
index 0000000..7c92052
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_invert_colors.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FF000000"
+        android:pathData="M18.939,7.244c-5.887,-5.885 -6.214,-6.214 -6.222,-6.222l-0.707,-0.737L5.088,7.207c-2.914,2.915 -3.74,6.629 -2.266,10.19c1.541,3.719 5.312,6.316 9.174,6.317l0.0,0.0c3.861,-0.001 7.636,-2.603 9.179,-6.328C22.646,13.834 21.832,10.138 18.939,7.244zM4.67,16.632c-1.149,-2.776 -0.481,-5.696 1.832,-8.011l5.494,-5.492c0.0,0.002 0.002,0.003 0.003,0.004l0.0,18.582c-0.001,0.0 -0.002,0.0 -0.003,0.0C8.922,21.714 5.91,19.624 4.67,16.632z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location.xml b/packages/SystemUI/res/drawable/ic_qs_location.xml
new file mode 100644
index 0000000..e6e98a0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_location.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FF000000"
+        android:pathData="M12.0,2.0C8.1,2.0 5.0,5.1 5.0,9.0c0.0,5.2 7.0,13.0 7.0,13.0s7.0,-7.8 7.0,-13.0C19.0,5.1 15.9,2.0 12.0,2.0zM12.0,11.5c-1.4,0.0 -2.5,-1.1 -2.5,-2.5s1.1,-2.5 2.5,-2.5c1.4,0.0 2.5,1.1 2.5,2.5S13.4,11.5 12.0,11.5z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_minus.xml b/packages/SystemUI/res/drawable/ic_qs_minus.xml
new file mode 100644
index 0000000..8323e89
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_minus.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FF000000"
+        android:pathData="M7.0,11.0l0.0,2.0l10.0,0.0l0.0,-2.0L7.0,11.0zM12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,20.0c-4.4,0.0 -8.0,-3.6 -8.0,-8.0s3.6,-8.0 8.0,-8.0c4.4,0.0 8.0,3.6 8.0,8.0S16.4,20.0 12.0,20.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_plus.xml b/packages/SystemUI/res/drawable/ic_qs_plus.xml
new file mode 100644
index 0000000..84cd72a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_plus.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FF000000"
+        android:pathData="M13.0,7.0l-2.0,0.0l0.0,4.0L7.0,11.0l0.0,2.0l4.0,0.0l0.0,4.0l2.0,0.0l0.0,-4.0l4.0,0.0l0.0,-2.0l-4.0,0.0L13.0,7.0zM12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,20.0c-4.4,0.0 -8.0,-3.6 -8.0,-8.0s3.6,-8.0 8.0,-8.0c4.4,0.0 8.0,3.6 8.0,8.0S16.4,20.0 12.0,20.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml b/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml
new file mode 100644
index 0000000..fa6f20c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FF000000"
+        android:pathData="M3.0,9.0c0.0,0.0 0.0,6.0 0.0,6.0l4.0,0.0l5.0,5.0L12.0,4.0L7.0,9.0L3.0,9.0zM16.5,12.0c0.0,-1.8 -1.0,-3.3 -2.5,-4.0L14.0,16.0C15.5,15.3 16.5,13.8 16.5,12.0zM14.0,3.2l0.0,2.1c2.9,0.9 5.0,3.5 5.0,6.7s-2.1,5.8 -5.0,6.7l0.0,2.1c4.0,-0.9 7.0,-4.5 7.0,-8.8S18.0,4.1 14.0,3.2z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_ringer_silent.xml b/packages/SystemUI/res/drawable/ic_qs_ringer_silent.xml
new file mode 100644
index 0000000..0665196
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_ringer_silent.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FF000000"
+        android:pathData="M16.5,12.0c0.0,-1.8 -1.0,-3.3 -2.5,-4.0l0.0,2.2l2.5,2.5C16.5,12.4 16.5,12.2 16.5,12.0zM19.0,12.0c0.0,0.9 -0.2,1.8 -0.5,2.6l1.5,1.5c0.7,-1.2 1.0,-2.7 1.0,-4.2c0.0,-4.3 -3.0,-7.9 -7.0,-8.8l0.0,2.1C16.9,6.2 19.0,8.8 19.0,12.0zM4.3,3.0L3.0,4.3L7.7,9.0L3.0,9.0c0.0,0.0 0.0,6.0 0.0,6.0l4.0,0.0l5.0,5.0l0.0,-6.7l4.3,4.3c-0.7,0.5 -1.4,0.9 -2.3,1.2l0.0,2.1c1.4,-0.3 2.6,-0.9 3.7,-1.8l2.0,2.0l1.3,-1.3l-9.0,-9.0L4.3,3.0zM12.0,4.0L9.9,6.1L12.0,8.2L12.0,4.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_ringer_vibrate.xml b/packages/SystemUI/res/drawable/ic_qs_ringer_vibrate.xml
new file mode 100644
index 0000000..299a2ef
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_ringer_vibrate.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FF000000"
+        android:pathData="M0.0,15.0l2.0,0.0L2.0,9.0L0.0,9.0L0.0,15.0zM3.0,17.0l2.0,0.0L5.0,7.0L3.0,7.0L3.0,17.0zM22.0,9.0l0.0,6.0l2.0,0.0L24.0,9.0L22.0,9.0zM19.0,17.0l2.0,0.0L21.0,7.0l-2.0,0.0L19.0,17.0zM16.5,3.0l-9.0,0.0C6.7,3.0 6.0,3.7 6.0,4.5l0.0,15.0C6.0,20.3 6.7,21.0 7.5,21.0l9.0,0.0c0.8,0.0 1.5,-0.7 1.5,-1.5l0.0,-15.0C18.0,3.7 17.3,3.0 16.5,3.0zM16.0,19.0L8.0,19.0L8.0,5.0l8.0,0.0L16.0,19.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation.xml b/packages/SystemUI/res/drawable/ic_qs_rotation.xml
new file mode 100644
index 0000000..18c5922
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_rotation.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FF000000"
+        android:pathData="M16.5,2.5c3.3,1.5 5.6,4.7 6.0,8.5l1.5,0.0C23.4,4.8 18.3,0.0 12.0,0.0c-0.2,0.0 -0.4,0.0 -0.7,0.0l3.8,3.8L16.5,2.5zM10.2,1.7c-0.6,-0.6 -1.5,-0.6 -2.1,0.0L1.7,8.1c-0.6,0.6 -0.6,1.5 0.0,2.1l12.0,12.0c0.6,0.6 1.5,0.6 2.1,0.0l6.4,-6.4c0.6,-0.6 0.6,-1.5 0.0,-2.1L10.2,1.7zM14.8,21.2l-12.0,-12.0l6.4,-6.4l12.0,12.0L14.8,21.2zM7.5,21.5c-3.3,-1.5 -5.6,-4.7 -6.0,-8.5L0.1,13.0C0.6,19.2 5.7,24.0 12.0,24.0c0.2,0.0 0.4,0.0 0.7,0.0l-3.8,-3.8L7.5,21.5z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_lock.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_lock.xml
new file mode 100644
index 0000000..1068f30
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_rotation_lock.xml
@@ -0,0 +1,34 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FF000000"
+        android:pathData="M16.5,2.5c3.3,1.5 5.6,4.7 6.0,8.5L24.0,11.0C23.4,4.8 18.3,0.0 12.0,0.0c-0.2,0.0 -0.4,0.0 -0.7,0.0l3.8,3.8L16.5,2.5zM7.5,21.5c-3.3,-1.5 -5.6,-4.7 -6.0,-8.5L0.1,13.0C0.6,19.2 5.7,24.0 12.0,24.0c0.2,0.0 0.4,0.0 0.7,0.0l-3.8,-3.8L7.5,21.5z"/>
+    <path
+        android:pathData="M12.05,7.7c-1.104,0,-2,0.945,-2,2.05v0.5h4v-0.5C14.05,8.646,13.154,7.7,12.05,7.7z"
+        android:fill="#00000000"/>
+    <path
+        android:fill="#FF000000"
+        android:pathData="M15.05,10.25l0.0,-0.5c0.0,-1.656 -1.343,-3.0 -3.0,-3.0c-1.657,0.0 -2.995,1.344 -2.995,3.0l-0.005,0.5c-0.552,0.0 -1.0,0.448 -1.0,1.0l0.0,5.0c0.0,0.553 0.448,1.0 1.0,1.0l6.0,0.0c0.552,0.0 1.0,-0.447 1.0,-1.0l0.0,-5.0C16.05,10.698 15.602,10.25 15.05,10.25zM12.05,14.75c-0.552,0.0 -1.0,-0.447 -1.0,-1.0c0.0,-0.552 0.448,-1.0 1.0,-1.0s1.0,0.448 1.0,1.0C13.05,14.303 12.602,14.75 12.05,14.75zM14.05,10.25l-4.0,0.0l0.0,-0.5c0.0,-1.104 0.896,-2.05 2.0,-2.05c1.104,0.0 2.0,0.945 2.0,2.05L14.05,10.25z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_locked_landscape.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_locked_landscape.xml
new file mode 100644
index 0000000..532a2ed
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_rotation_locked_landscape.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FF000000"
+        android:pathData="M21.0,5.0L3.0,5.0C1.9,5.0 1.0,5.9 1.0,7.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l18.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L23.0,7.0C23.0,5.9 22.1,5.0 21.0,5.0zM19.0,17.0L5.0,17.0L5.0,7.0l14.0,0.0L19.0,17.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_locked_portrait.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_locked_portrait.xml
new file mode 100644
index 0000000..496bb0e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_rotation_locked_portrait.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FF000000"
+        android:pathData="M17.0,1.0L7.0,1.0C5.9,1.0 5.0,1.9 5.0,3.0l0.0,18.0c0.0,1.1 0.9,2.0 2.0,2.0l10.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L19.0,3.0C19.0,1.9 18.1,1.0 17.0,1.0zM17.0,19.0L7.0,19.0L7.0,5.0l10.0,0.0L17.0,19.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_zen.xml b/packages/SystemUI/res/drawable/ic_qs_zen.xml
new file mode 100644
index 0000000..059c068
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_zen.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FF000000"
+        android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,16.0l0.0,-5.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0L18.0,16.0zM14.0,9.8l-2.8,3.4L14.0,13.200001L14.0,15.0L9.0,15.0l0.0,-1.8l2.8,-3.4L9.0,9.799999L9.0,8.0l5.0,0.0L14.0,9.8z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/qs_panel_background.xml b/packages/SystemUI/res/drawable/qs_panel_background.xml
new file mode 100644
index 0000000..c324976
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_panel_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+        android:insetLeft="@dimen/notification_side_padding"
+        android:insetRight="@dimen/notification_side_padding">
+    <shape>
+        <solid android:color="@color/system_primary_color" />
+        <corners android:radius="@*android:dimen/notification_quantum_rounded_rect_radius" />
+    </shape>
+</inset>
diff --git a/packages/SystemUI/res/layout/flip_settings.xml b/packages/SystemUI/res/layout/flip_settings.xml
deleted file mode 100644
index 28d9625..0000000
--- a/packages/SystemUI/res/layout/flip_settings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-
-<com.android.systemui.statusbar.phone.QuickSettingsContainerView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/quick_settings_container"
-    android:padding="@dimen/notification_side_padding"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:background="#5f000000"
-    android:animateLayoutChanges="true"
-    android:columnCount="@integer/quick_settings_num_columns" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
new file mode 100644
index 0000000..b24d4ad
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<FrameLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/quick_settings_container"
+        android:paddingLeft="@dimen/notification_side_padding"
+        android:paddingRight="@dimen/notification_side_padding"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@drawable/qs_panel_background" >
+    <com.android.systemui.qs.QSPanel
+            android:id="@+id/quick_settings_panel"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/qs_zen_mode_detail.xml b/packages/SystemUI/res/layout/qs_zen_mode_detail.xml
new file mode 100644
index 0000000..2df6d43
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_zen_mode_detail.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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.
+-->
+<com.android.systemui.qs.tiles.ZenModeDetail xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/system_secondary_color" >
+
+    <com.android.systemui.qs.QSImageView
+        android:id="@android:id/button1"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_alignParentStart="true"
+        android:padding="@dimen/quick_settings_panel_padding" />
+
+    <Switch
+        android:id="@android:id/checkbox"
+        android:layout_width="wrap_content"
+        android:layout_height="64dp"
+        android:layout_alignParentEnd="true"
+        android:gravity="center"
+        android:padding="@dimen/quick_settings_panel_padding" />
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="match_parent"
+        android:layout_height="64dp"
+        android:layout_toEndOf="@android:id/button1"
+        android:layout_toStartOf="@android:id/checkbox"
+        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
+        android:gravity="center_vertical"
+        android:paddingStart="@dimen/quick_settings_panel_padding"
+        android:text="@string/zen_mode_title" />
+
+    <View
+        android:id="@android:id/custom"
+        android:layout_width="match_parent"
+        android:layout_height="2dp"
+        android:layout_below="@android:id/title"
+        android:background="#888" />
+
+    <ListView
+        android:id="@android:id/content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_above="@android:id/button2"
+        android:layout_below="@android:id/custom"
+        android:divider="#00000000"
+        android:dividerHeight="0px" />
+
+    <TextView
+        android:id="@android:id/button2"
+        style="@style/QSBorderless"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentEnd="true"
+        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
+        android:padding="@dimen/quick_settings_panel_padding"
+        android:text="@string/quick_settings_more_settings"
+        android:textAllCaps="true" />
+
+</com.android.systemui.qs.tiles.ZenModeDetail>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_zen_mode_detail_condition.xml b/packages/SystemUI/res/layout/qs_zen_mode_detail_condition.xml
new file mode 100644
index 0000000..a5c8903
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_zen_mode_detail_condition.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+
+    <RadioButton
+        android:id="@android:id/checkbox"
+        android:layout_width="32dp"
+        android:layout_height="64dp"
+        android:layout_alignParentStart="true"
+        android:layout_marginStart="@dimen/quick_settings_panel_padding"
+        android:gravity="center" />
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="match_parent"
+        android:layout_height="64dp"
+        android:layout_toEndOf="@android:id/checkbox"
+        android:layout_toStartOf="@android:id/button1"
+        android:ellipsize="end"
+        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
+        android:gravity="center_vertical"
+        android:maxLines="1"
+        android:text="@string/accessibility_back" />
+
+    <com.android.systemui.qs.QSImageView
+        android:id="@android:id/button1"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_alignParentEnd="true"
+        android:layout_marginEnd="48dp"
+        android:padding="@dimen/quick_settings_panel_padding"
+        android:paddingRight="0px" />
+
+    <com.android.systemui.qs.QSImageView
+        android:id="@android:id/button2"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_alignParentEnd="true"
+        android:padding="@dimen/quick_settings_panel_padding" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_settings_tile.xml b/packages/SystemUI/res/layout/quick_settings_tile.xml
deleted file mode 100644
index 911f6a2..0000000
--- a/packages/SystemUI/res/layout/quick_settings_tile.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<com.android.systemui.statusbar.phone.QuickSettingsTileView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="@dimen/quick_settings_cell_height"
-    android:background="@drawable/qs_tile_background" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_settings_tile_alarm.xml b/packages/SystemUI/res/layout/quick_settings_tile_alarm.xml
deleted file mode 100644
index 493c704..0000000
--- a/packages/SystemUI/res/layout/quick_settings_tile_alarm.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<TextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    style="@style/TextAppearance.QuickSettings.TileView.AllInOne"
-    android:id="@+id/alarm_textview"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center"
-    android:gravity="center"
-    android:drawableTop="@drawable/ic_qs_alarm_on"
-    />
diff --git a/packages/SystemUI/res/layout/quick_settings_tile_basic.xml b/packages/SystemUI/res/layout/quick_settings_tile_basic.xml
deleted file mode 100644
index 16bf49c..0000000
--- a/packages/SystemUI/res/layout/quick_settings_tile_basic.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:layout_gravity="top"
-    android:orientation="vertical">
-    <ImageView
-        android:id="@+id/image"
-        android:layout_marginTop="@dimen/qs_tile_margin_above_icon"
-        android:layout_marginBottom="@dimen/qs_tile_margin_below_icon"
-        android:layout_width="@dimen/qs_tile_icon_size"
-        android:layout_height="@dimen/qs_tile_icon_size"
-        android:layout_gravity="top|center_horizontal"
-        android:scaleType="centerInside"
-        />
-    <TextView
-        style="@style/TextAppearance.QuickSettings.TileView"
-        android:id="@+id/text"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top|center_horizontal"
-        android:gravity="top|center_horizontal"
-        />
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_settings_tile_battery.xml b/packages/SystemUI/res/layout/quick_settings_tile_battery.xml
deleted file mode 100644
index 1f39aef..0000000
--- a/packages/SystemUI/res/layout/quick_settings_tile_battery.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_gravity="top"
-        android:orientation="vertical">
-    <com.android.systemui.BatteryMeterView
-            android:id="@+id/image"
-            android:layout_marginTop="@dimen/qs_tile_margin_above_icon"
-            android:layout_marginBottom="@dimen/qs_tile_margin_below_icon"
-            android:layout_width="22dp"
-            android:layout_height="32dp"
-            android:padding="3dp"
-            android:layout_gravity="top|center_horizontal"
-            systemui:frameColor="@color/qs_batterymeter_frame_color"
-            />
-    <TextView
-            style="@style/TextAppearance.QuickSettings.TileView"
-            android:id="@+id/text"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="top|center_horizontal"
-            android:gravity="top|center_horizontal"
-            />
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_settings_tile_ime.xml b/packages/SystemUI/res/layout/quick_settings_tile_ime.xml
deleted file mode 100644
index 1a31efa5..0000000
--- a/packages/SystemUI/res/layout/quick_settings_tile_ime.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<TextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    style="@style/TextAppearance.QuickSettings.TileView.AllInOne"
-    android:id="@+id/ime_textview"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center"
-    android:gravity="center"
-    android:drawableTop="@drawable/ic_qs_ime"
-    android:text="@string/quick_settings_ime_label"
-    />
diff --git a/packages/SystemUI/res/layout/quick_settings_tile_media.xml b/packages/SystemUI/res/layout/quick_settings_tile_media.xml
deleted file mode 100644
index 355176c6..0000000
--- a/packages/SystemUI/res/layout/quick_settings_tile_media.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<TextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    style="@style/TextAppearance.QuickSettings.TileView.AllInOne"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:gravity="center"
-    android:text="@string/quick_settings_media_device_label"
-    android:singleLine="true"
-    />
diff --git a/packages/SystemUI/res/layout/quick_settings_tile_monitoring.xml b/packages/SystemUI/res/layout/quick_settings_tile_monitoring.xml
deleted file mode 100644
index 4fa48eb..0000000
--- a/packages/SystemUI/res/layout/quick_settings_tile_monitoring.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:layout_gravity="top"
-    android:orientation="vertical">
-    <ImageView
-        android:id="@+id/image"
-        android:layout_marginTop="@dimen/qs_tile_margin_above_icon"
-        android:layout_marginBottom="@dimen/qs_cawarn_tile_margin_below_icon"
-        android:layout_width="@dimen/qs_tile_icon_size"
-        android:layout_height="@dimen/qs_tile_icon_size"
-        android:layout_gravity="top|center_horizontal"
-        android:scaleType="centerInside"
-        />
-    <TextView
-        style="@style/TextAppearance.QuickSettings.CaCertWarning"
-        android:id="@+id/text"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top|center_horizontal"
-        android:gravity="top|center_horizontal"
-        />
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_settings_tile_rssi.xml b/packages/SystemUI/res/layout/quick_settings_tile_rssi.xml
deleted file mode 100644
index 6bf31e0..0000000
--- a/packages/SystemUI/res/layout/quick_settings_tile_rssi.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<RelativeLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:layout_gravity="top">
-    <FrameLayout
-        android:id="@+id/rssi_images"
-        android:layout_marginTop="@dimen/qs_tile_margin_above_icon"
-        android:layout_marginBottom="@dimen/qs_tile_margin_below_icon"
-        android:layout_width="@dimen/qs_tile_icon_size"
-        android:layout_height="@dimen/qs_tile_icon_size"
-        android:layout_gravity="top|center_horizontal"
-        android:layout_centerHorizontal="true"
-        >
-        <ImageView
-            android:id="@+id/rssi_image"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_centerInParent="true"
-            />
-        <ImageView
-            android:id="@+id/rssi_overlay_image"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_centerInParent="true"
-            />
-    </FrameLayout>
-    <ImageView
-            android:id="@+id/activity_in"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:src="@drawable/ic_qs_signal_in"
-            android:layout_toRightOf="@id/rssi_images"
-            android:layout_alignBottom="@id/rssi_images"
-            />
-    <ImageView
-            android:id="@+id/activity_out"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:src="@drawable/ic_qs_signal_out"
-            android:layout_toRightOf="@id/rssi_images"
-            android:layout_alignBottom="@id/rssi_images"
-            />
-    <TextView
-        style="@style/TextAppearance.QuickSettings.TileView"
-        android:id="@+id/rssi_textview"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top|center_horizontal"
-        android:gravity="top|center_horizontal"
-        android:text="@string/quick_settings_rssi_label"
-        android:layout_centerHorizontal="true"
-        android:layout_below="@id/rssi_images"
-        android:textAllCaps="@bool/quick_settings_rssi_tile_capitalization"
-        />
-</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_settings_tile_user.xml b/packages/SystemUI/res/layout/quick_settings_tile_user.xml
deleted file mode 100644
index 80fc685..0000000
--- a/packages/SystemUI/res/layout/quick_settings_tile_user.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-    <ImageView
-        android:id="@+id/user_imageview"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:src="@drawable/ic_qs_default_user"
-        android:scaleType="centerCrop"
-        />
-    <TextView
-        style="@style/TextAppearance.QuickSettings.TileView.User"
-        android:id="@+id/user_textview"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal|bottom"
-        android:gravity="center"
-        android:text="@string/quick_settings_user_label"
-        />
-</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_settings_tile_wifi.xml b/packages/SystemUI/res/layout/quick_settings_tile_wifi.xml
deleted file mode 100644
index e61c595..0000000
--- a/packages/SystemUI/res/layout/quick_settings_tile_wifi.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<RelativeLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:layout_gravity="top">
-    <ImageView
-        android:id="@+id/image"
-        android:layout_marginTop="@dimen/qs_tile_margin_above_icon"
-        android:layout_marginBottom="@dimen/qs_tile_margin_below_icon"
-        android:layout_width="@dimen/qs_tile_icon_size"
-        android:layout_height="@dimen/qs_tile_icon_size"
-        android:layout_gravity="top|center_horizontal"
-        android:layout_centerHorizontal="true"
-        android:scaleType="centerInside"
-        />
-    <ImageView
-            android:id="@+id/activity_in"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:src="@drawable/ic_qs_wifi_in"
-            android:layout_toRightOf="@id/image"
-            android:layout_alignBottom="@id/image"
-            />
-    <ImageView
-            android:id="@+id/activity_out"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:src="@drawable/ic_qs_wifi_out"
-            android:layout_toRightOf="@id/image"
-            android:layout_alignBottom="@id/image"
-            />
-    <TextView
-        style="@style/TextAppearance.QuickSettings.TileView"
-        android:id="@+id/text"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top|center_horizontal"
-        android:gravity="top|center_horizontal"
-        android:layout_centerHorizontal="true"
-        android:layout_below="@id/image" 
-        />
-</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index f045da4..2ec9935 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -67,7 +67,7 @@
                 android:layout_height="wrap_content"
                 android:orientation="vertical">
                 <include
-                    layout="@layout/flip_settings"
+                    layout="@layout/qs_panel"
                     android:layout_marginTop="@dimen/status_bar_header_height_expanded"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"/>
diff --git a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
index 0e84762..7671c354a 100644
--- a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
+++ b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
@@ -29,6 +29,7 @@
         android:layout_alignParentBottom="true"
         android:button="@null"
         android:background="@*android:drawable/switch_track_quantum"
+        android:visibility="gone"
         />
     <com.android.systemui.settings.ToggleSeekBar
         android:id="@+id/slider"
@@ -36,6 +37,7 @@
         android:layout_height="wrap_content"
         android:layout_toEndOf="@id/toggle"
         android:layout_centerVertical="true"
+        android:layout_alignParentStart="true"
         android:layout_alignParentEnd="true"
         android:paddingStart="20dp"
         android:paddingEnd="20dp"
@@ -51,5 +53,6 @@
         android:paddingTop="26dp"
         android:textColor="#666666"
         android:textSize="12sp"
+        android:visibility="gone"
         />
 </merge>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 8ec48c8..eb66a59 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Onlangse programme"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Deursoek"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Foon"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Knoppie vir wissel van invoermetode."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Versoenbaarheid-zoem se knoppie."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoem kleiner na groter skerm."</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 7aaaaf3..a3eca6a 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"የቅርብ ጊዜ  መተግበሪያዎች"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"ፈልግ"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"ካሜራ"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"ስልክ"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"የግቤት ስልት አዝራር ቀይር"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"የተኳኋኝአጉላ አዝራር።"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"አነስተኛውን ማያ ወደ ትልቅ አጉላ።"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index febcab6..e03a2f9 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Скорошни приложения"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Търсене"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Камера"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Телефон"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Бутон за превключване на метода на въвеждане."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Бутон за промяна на мащаба с цел съвместимост."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Промяна на мащаба на екрана от по-малък до по-голям."</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 658ece0..e50a155 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Aplicacions recents"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Cerca"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Càmera"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telèfon"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Botó de canvi del mètode d\'entrada."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botó de zoom de compatibilitat."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Amplia menys com més gran sigui la pantalla."</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index c7d2703..a2d9d9c 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Nové aplikace"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Hledat"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Fotoaparát"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Tlačítko přepnutí metody zadávání"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Tlačítko úpravy velikosti z důvodu kompatibility"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zvětšit menší obrázek na větší obrazovku."</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 51198e6..00041a2 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Aplicaciones recientes"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Buscar"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Cámara"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Teléfono"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Botón Cambiar método de entrada"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botón de zoom de compatibilidad"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de pantalla más pequeña a más grande"</string>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 0f95c60..4c42a32 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Hiljutised rakendused"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Otsing"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Kaamera"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Sisestusmeetodi vahetamise nupp."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Sobivussuumi nupp."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Suumi suuremale ekraanile vähem."</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index f36d04b..a9c6af7a 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"برنامه‌های اخیر"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"جستجو"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"دوربین"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"تلفن"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"کلید تغییر روش ورود متن."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"دکمه بزرگنمایی سازگار."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"بزرگنمایی از صفحه‌های کوچک تا بزرگ."</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 4ae3851..8ddf070 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Viimeaikaiset sovellukset"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Haku"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Puhelin"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Syöttötavan vaihtopainike."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Yhteensopivuuszoomaus-painike."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoomaa pienemmältä suuremmalle ruudulle."</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index c55a5fc..69b5acc 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Applications récentes"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Rechercher"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Appareil photo"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Téléphone"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Bouton \"Changer le mode de saisie\""</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Bouton \"Zoom de compatibilité\""</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de compatibilité avec la taille de l\'écran"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 1121918..85f817b 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Applications récentes"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Rechercher"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Appareil photo"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Téléphoner"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Bouton \"Changer le mode de saisie\""</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Bouton \"Zoom de compatibilité\""</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de compatibilité avec la taille de l\'écran"</string>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index 7a8ab10..8c26a8a 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Վերջին ծրագրերը"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Որոնել"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Ֆոտոխցիկ"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Հեռախոս"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Միացնել մուտքագրման եղանակի կոճակը:"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Համատեղելիության խոշորացման կոճակը:"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Դիտափոխել փոքրից ավելի մեծ էկրան:"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 08ffc54..e1625076 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"אפליקציות אחרונות"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"חפש"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"מצלמה"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"טלפון"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"לחצן החלפת שיטת קלט."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"לחצן מרחק מתצוגה של תאימות."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"שנה מרחק מתצוגה של מסך קטן לגדול יותר."</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 2caa069..a9e7935 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"最近使ったアプリ"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"検索"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"カメラ"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"電話"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"入力方法の切り替えボタン。"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"互換ズームボタン。"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"小さい画面から大きい画面に拡大。"</string>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 62e40d0..c56864b 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"ბოლოს გამოყენებული აპები"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"ძიება"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"კამერა"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"ტელეფონი"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"შეყვანის მეთოდის გადართვის ღილაკი."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"თავსებადი მასშტაბირების ღილაკი."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"შეცვალეთ პატარა ეკრანი უფრო დიდით."</string>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index 35a6917..a5008b3 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -64,7 +64,7 @@
     <string name="screenshot_saving_ticker" msgid="7403652894056693515">"កំពុង​រក្សាទុក​រូបថត​អេក្រង់…"</string>
     <string name="screenshot_saving_title" msgid="8242282144535555697">"កំពុង​រក្សាទុក​រូបថត​អេក្រង់..."</string>
     <string name="screenshot_saving_text" msgid="2419718443411738818">"រូបថត​អេក្រង់​កំពុង​ត្រូវ​បាន​រក្សាទុក។"</string>
-    <string name="screenshot_saved_title" msgid="6461865960961414961">"បាន​ចាប់​យក​រូបថត​អេក្រង់។"</string>
+    <string name="screenshot_saved_title" msgid="6461865960961414961">"បាន​ចាប់​យក​រូបថត​អេក្រង់។​"</string>
     <string name="screenshot_saved_text" msgid="1152839647677558815">"ប៉ះ ​ដើម្បី​មើល​រូបថត​អេក្រង់​របស់​អ្នក​។"</string>
     <string name="screenshot_failed_title" msgid="705781116746922771">"មិន​អាច​ចាប់​យក​រូប​ថត​អេក្រង់​។"</string>
     <string name="screenshot_failed_text" msgid="8134011269572415402">"មិន​អាច​រក្សាទុក​រូបថត​អេក្រង់​។ ឧបករណ៍​ផ្ទុក​អាច​កំពុង​ប្រើ​​។"</string>
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"កម្មវិធី​ថ្មីៗ"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"ស្វែងរក"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"ម៉ាស៊ីន​ថត"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"ទូរស័ព្ទ"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"ប្ដូរ​ប៊ូតុង​វិធីសាស្ត្រ​បញ្ចូល។"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ប៊ូតុង​ពង្រីក​ត្រូវ​គ្នា។"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ពង្រីក/បង្រួម​​អេក្រង់​ពី​​ទៅធំ"</string>
@@ -141,7 +140,7 @@
     <string name="accessibility_remove_notification" msgid="3603099514902182350">"សម្អាត​ការ​ជូន​ដំណឹង។"</string>
     <string name="accessibility_gps_enabled" msgid="3511469499240123019">"បាន​បើក GPS ។"</string>
     <string name="accessibility_gps_acquiring" msgid="8959333351058967158">"ទទួល​​ GPS ។"</string>
-    <string name="accessibility_tty_enabled" msgid="4613200365379426561">"បាន​បើក​ម៉ាស៊ីន​អង្គុលីលេខ"</string>
+    <string name="accessibility_tty_enabled" msgid="4613200365379426561">"បាន​បើក​ម៉ាស៊ីន​អង្គុលីលេខ​"</string>
     <string name="accessibility_ringer_vibrate" msgid="666585363364155055">"កម្មវិធី​រោទ៍​ញ័រ។"</string>
     <string name="accessibility_ringer_silent" msgid="9061243307939135383">"កម្មវិធី​រោទ៍​ស្ងាត់។"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> បដិសេធ។"</string>
@@ -188,7 +187,7 @@
     <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"ចាក់​សោ​​បញ្ឈរ"</string>
     <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"ចាក់​សោ​​​ផ្ដេក"</string>
     <string name="quick_settings_ime_label" msgid="7073463064369468429">"វិធីសាស្ត្រ​បញ្ចូល"</string>
-    <string name="quick_settings_location_label" msgid="5011327048748762257">"ទី​តាំង"</string>
+    <string name="quick_settings_location_label" msgid="5011327048748762257">"ទី​តាំង​"</string>
     <string name="quick_settings_location_off_label" msgid="7464544086507331459">"ទីតាំង​បាន​បិទ"</string>
     <string name="quick_settings_media_device_label" msgid="1302906836372603762">"ឧបករណ៍​មេឌៀ"</string>
     <string name="quick_settings_rssi_label" msgid="7725671335550695589">"RSSI"</string>
@@ -209,7 +208,7 @@
     <string name="recents_empty_message" msgid="2269156590813544104">"ថ្មីៗ"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"ព័ត៌មាន​កម្មវិធី"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ស្វែងរក"</string>
-    <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"បណ្ដាញ​អាច​\nត្រូវ​បាន​ត្រួតពិនិត្យ"</string>
+    <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"បណ្ដាញ​អាច​\nត្រូវ​បាន​ត្រួតពិនិត្យ​"</string>
     <string name="description_target_search" msgid="3091587249776033139">"ស្វែងរក"</string>
     <string name="description_direction_up" msgid="7169032478259485180">"រុញ​ឡើង​លើ​ដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ។"</string>
     <string name="description_direction_left" msgid="7207478719805562165">"រុញ​ទៅ​ឆ្វេង​ដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ។"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index d7cd13b..bd7a257 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"최근에 사용한 앱"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"검색"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"카메라"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"전화"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"입력 방법 버튼을 전환합니다."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"호환성 확대/축소 버튼입니다."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"작은 화면을 큰 화면으로 확대합니다."</string>
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index 7223773..5755029 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -25,7 +25,7 @@
     <integer name="status_bar_recents_bg_gradient_degrees">90</integer>
 
     <!-- The number of columns in the QuickSettings -->
-    <integer name="quick_settings_num_columns">6</integer>
+    <integer name="quick_settings_num_columns">4</integer>
 
     <!-- The maximum number of rows in the QuickSettings -->
     <integer name="quick_settings_max_rows">2</integer>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 87087b4..b160a5a 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -181,7 +181,7 @@
     <string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"„Bluetooth“ (<xliff:g id="NUMBER">%d</xliff:g> įreng.)"</string>
     <string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"„Bluetooth“ išjungta"</string>
-    <string name="quick_settings_brightness_label" msgid="6968372297018755815">"Skaistis"</string>
+    <string name="quick_settings_brightness_label" msgid="6968372297018755815">"Šviesumas"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Automatiškai sukti"</string>
     <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Sukimas užrakintas"</string>
     <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Užrakinta stačia padėtis"</string>
@@ -200,7 +200,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Tinklo nėra"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"„Wi-Fi“ išjungta"</string>
     <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Perduoti ekraną"</string>
-    <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Skaistis"</string>
+    <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Šviesumas"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATINIS"</string>
     <string name="quick_settings_inversion_label" msgid="1666358784283020762">"Spalvų inversijos režimas"</string>
     <string name="quick_settings_contrast_label" msgid="3319507551689108692">"Patobulinto kontrasto režimas"</string>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index 3e7b660..4669d99 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Сүүлийн апп"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Хайх"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Камер"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Утас"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Оруулах аргыг сэлгэх товч."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Тохиромжтой өсгөх товч."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Жижгээс том дэлгэцрүү өсгөх."</string>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index af50786..60e5527 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Aplikasi terbaharu"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Cari"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Butang tukar kaedah input."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Butang zum keserasian."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Skrin zum lebih kecil kepada lebih besar."</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index e51341d..00bea9c 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Ostatnie aplikacje"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Szukaj"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Aparat"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Przycisk przełączania metody wprowadzania."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Przycisk powiększenia na potrzeby zgodności."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Powiększa mniejszy ekran do większego."</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 162d763..2337a7d 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Aplicativos recentes"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Pesquisar"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Câmera"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefone"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Alterar botão do método de entrada."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botão de zoom da compatibilidade."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Aumentar a tela com zoom."</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 75ba35b..4f2c471 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Aplicaţii recente"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Căutați"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Cameră foto"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Buton pentru comutarea metodei de introducere."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Buton zoom pentru compatibilitate."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Faceţi zoom de la o imagine mai mică la una mai mare."</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 6bdc6f4..837e98e 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Недавние приложения"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Поиск"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Камера"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Телефон."</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Кнопка переключения способа ввода."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Кнопка масштабирования (режим совместимости)"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Уменьшение изображения для увеличения свободного места на экране."</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 12c90ec..ae70ed6 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Nové aplikácie"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Hľadať"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Fotoaparát"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefón"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Tlačidlo prepnutia metódy vstupu."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Tlačidlo úpravy veľkosti z dôvodu kompatibility."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zväčšiť menší obrázok na väčšiu obrazovku."</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index b626f78..2713cf9 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -76,8 +76,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Programu za hivi karibuni"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Tafuta"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Simu"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Swichi kitufe cha mbinu ingizi."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Kichupo cha kukuza kwa utangamanifu"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Kuza kidogo kwa skrini kubwa."</string>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index fe2224e..6dea81f 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -21,7 +21,7 @@
      for different hardware and product builds. -->
 <resources>
     <!-- The number of columns in the QuickSettings -->
-    <integer name="quick_settings_num_columns">3</integer>
+    <integer name="quick_settings_num_columns">4</integer>
 
     <!-- The maximum number of rows in the QuickSettings -->
     <integer name="quick_settings_max_rows">4</integer>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 6cb4a48..4dc3d22 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"แอปพลิเคชันล่าสุด"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"ค้นหา"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"กล้องถ่ายรูป"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"โทรศัพท์"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"ปุ่มสลับวิธีการป้อนข้อมูล"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ปุ่มซูมที่ใช้งานร่วมกันได้"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ซูมหน้าจอให้มีขนาดใหญ่ขึ้น"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 17e720e..e50f723 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Kamakailang apps"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Hanapin"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Camera"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telepono"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Ilipat ang button na pamamaraan ng pag-input."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Button ng zoom ng pagiging tugma."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Mag-zoom nang mas maliit sa mas malaking screen."</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 1bf033f5..9100055 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Son uygulamalar"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Ara"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Giriş yöntemini değiştirme düğmesi."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Uyumluluk zum düğmesi."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Daha büyük ekrana daha küçük yakınlaştır."</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 8df2449..a4aadb3 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Ứng dụng gần đây"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Tìm kiếm"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Máy ảnh"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Điện thoại"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Nút chuyển phương thức nhập."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Nút thu phóng khả năng tương thích."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Thu phóng màn hình lớn hơn hoặc nhỏ hơn."</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 8643ca8..a5ae2b7 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"最近运行的应用"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"搜索"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"相机"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"电话"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"输入法切换按钮。"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"兼容性缩放按钮。"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"将小屏幕的图片放大在较大屏幕上显示。"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 07b7841..1e3f455 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"最近使用的應用程式"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"搜尋"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"相機"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"電話"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"切換輸入法按鈕。"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"相容性縮放按鈕。"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"將較小螢幕的畫面放大在較大螢幕上顯示。"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index c1a4e26..7de1bd0 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -36,6 +36,14 @@
     <color name="batterymeter_charge_color">#FFFFFFFF</color>
     <color name="batterymeter_bolt_color">#FFFFFFFF</color>
     <color name="qs_batterymeter_frame_color">#FF404040</color>
+    <color name="system_primary_color">#ff263238</color>
+    <color name="system_secondary_color">#ff384248</color>
+    <color name="system_accent_color">#ff7fcac3</color>
+    <color name="system_error_color">#fff0592b</color>
+    <color name="quick_settings_tile_icon_enabled">#ffffffff</color>
+    <color name="quick_settings_tile_icon_disabled">#ffcccccc</color>
+    <color name="quick_settings_tile_divider">#ff888888</color>
+    <color name="quick_settings_tile_text">#FFFFFFFF</color>
     <color name="status_bar_clock_color">#FFFFFFFF</color>
     <drawable name="notification_item_background_color">#ff111111</drawable>
     <drawable name="notification_item_background_color_pressed">#ff454545</drawable>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ab34030..79612e0 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -271,6 +271,10 @@
 
     <dimen name="quick_settings_tmp_scrim_stroke_width">8dp</dimen>
     <dimen name="quick_settings_tmp_scrim_text_size">30dp</dimen>
+    <dimen name="quick_settings_panel_padding">16dp</dimen>
+    <dimen name="quick_settings_tile_icon_outline">2dp</dimen>
+    <dimen name="quick_settings_tile_text_size">12sp</dimen>
+    <dimen name="quick_settings_tile_divider_height">1dp</dimen>
 
     <dimen name="notifications_top_padding">8dp</dimen>
     
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3d3cdf6..a50a0ac 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -503,9 +503,15 @@
     <!-- QuickSettings: Brightness dialog auto brightness button [CHAR LIMIT=NONE] -->
     <string name="quick_settings_brightness_dialog_auto_brightness_label">AUTO</string>
     <!-- QuickSettings: Label for the toggle that controls whether display inversion is enabled. [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_inversion_label">Color inversion mode</string>
+    <string name="quick_settings_inversion_label">Invert colors</string>
     <!-- QuickSettings: Label for the toggle that controls whether display color correction is enabled. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_color_space_label">Color correction mode</string>
+    <!-- QuickSettings: Control panel: Label for button that navigates to settings. [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_more_settings">More settings</string>
+    <!-- QuickSettings: Tethering. [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_tethering_label">Tethering</string>
+    <!-- QuickSettings: Hotspot. [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_hotspot_label">Hotspot</string>
 
     <!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
     <string name="recents_empty_message">RECENTS</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 4f52870..1273e74 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -180,4 +180,16 @@
     <style name="StatusBarHeader">
         <item name="android:layout_width">match_parent</item>
     </style>
+
+    <style name="QSWhiteTheme" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:colorControlNormal">#ffffffff</item>
+        <item name="android:colorControlActivated">#ffffffff</item>
+    </style>
+
+    <style name="QSAccentTheme" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:colorControlNormal">@color/system_accent_color</item>
+        <item name="android:colorControlActivated">@color/system_accent_color</item>
+    </style>
+
+    <style name="QSBorderless" parent="@android:style/Widget.Quantum.Button.Borderless" />
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/CircularClipper.java b/packages/SystemUI/src/com/android/systemui/qs/CircularClipper.java
new file mode 100644
index 0000000..16ee3b0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/CircularClipper.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.view.View;
+
+/** Helper for view-level circular clip animations. **/
+public class CircularClipper {
+
+    private final View mTarget;
+
+    private ValueAnimator mAnimator;
+
+    public CircularClipper(View target) {
+        mTarget = target;
+    }
+
+    public void animateCircularClip(int x, int y, boolean in, AnimatorListener listener) {
+        if (mAnimator != null) {
+            mAnimator.cancel();
+        }
+        final int w = mTarget.getWidth() - x;
+        final int h = mTarget.getHeight() - y;
+        int r = (int) Math.ceil(Math.sqrt(x * x + y * y));
+        r = (int) Math.max(r, Math.ceil(Math.sqrt(w * w + y * y)));
+        r = (int) Math.max(r, Math.ceil(Math.sqrt(w * w + h * h)));
+        r = (int) Math.max(r, Math.ceil(Math.sqrt(x * x + h * h)));
+
+        mAnimator = mTarget.createRevealAnimator(x, y, 0, r);
+        mAnimator.removeAllListeners();
+        if (listener != null) {
+            mAnimator.addListener(listener);
+        }
+        if (in) {
+            mAnimator.addListener(mVisibleOnStart);
+            mAnimator.start();
+        } else {
+            mAnimator.addListener(mGoneOnEnd);
+            mAnimator.reverse();
+        }
+    }
+
+    private final AnimatorListenerAdapter mVisibleOnStart = new AnimatorListenerAdapter() {
+        @Override
+        public void onAnimationStart(Animator animation) {
+            mTarget.setVisibility(View.VISIBLE);
+        }
+    };
+
+    private final AnimatorListenerAdapter mGoneOnEnd = new AnimatorListenerAdapter() {
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            mTarget.setVisibility(View.GONE);
+        };
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FilterCanvas.java b/packages/SystemUI/src/com/android/systemui/qs/FilterCanvas.java
new file mode 100644
index 0000000..05c8ee3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/FilterCanvas.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+
+/** Canvas that forwards calls to another canvas.  Can be subclassed to transform drawing calls.
+ * Temporary solution to runtime modification of a single drawable shape into two
+ * enabled & disabled versions.  See QSImageView. **/
+public class FilterCanvas extends Canvas {
+    private final Canvas mCanvas;
+
+    public FilterCanvas(Canvas c) {
+        mCanvas = c;
+    }
+
+    @Override
+    public void drawPath(Path path, Paint paint) {
+        mCanvas.drawPath(path, paint);
+    }
+
+    @Override
+    public int getSaveCount() {
+        return mCanvas.getSaveCount();
+    }
+
+    @Override
+    public int save() {
+        return mCanvas.save();
+    }
+
+    @Override
+    public void translate(float dx, float dy) {
+        mCanvas.translate(dx, dy);
+    }
+
+    @Override
+    public boolean clipRect(int left, int top, int right, int bottom) {
+        return mCanvas.clipRect(left, top, right, bottom);
+    }
+
+    @Override
+    public boolean clipRect(Rect rect) {
+        return mCanvas.clipRect(rect);
+    }
+
+    @Override
+    public void concat(Matrix matrix) {
+        mCanvas.concat(matrix);
+    }
+
+    @Override
+    public void restoreToCount(int saveCount) {
+        mCanvas.restoreToCount(saveCount);
+    }
+
+    @Override
+    public void drawRect(Rect r, Paint paint) {
+        mCanvas.drawRect(r, paint);
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/GlobalSetting.java b/packages/SystemUI/src/com/android/systemui/qs/GlobalSetting.java
new file mode 100644
index 0000000..1e15b9f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/GlobalSetting.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.Settings.Global;
+
+import com.android.systemui.statusbar.policy.Disposable;
+
+/** Helper for managing a global setting. **/
+public abstract class GlobalSetting extends ContentObserver implements Disposable {
+    private final Context mContext;
+    private final String mSettingName;
+
+    protected abstract void handleValueChanged(int value);
+
+    public GlobalSetting(Context context, Handler handler, String settingName) {
+        super(handler);
+        mContext = context;
+        mSettingName = settingName;
+        mContext.getContentResolver().registerContentObserver(
+                Global.getUriFor(mSettingName), false, this);
+    }
+
+    public int getValue() {
+        return Global.getInt(mContext.getContentResolver(), mSettingName, 0);
+    }
+
+    public void setValue(int value) {
+        Global.putInt(mContext.getContentResolver(), mSettingName, value);
+    }
+
+    @Override
+    public void dispose() {
+        mContext.getContentResolver().unregisterContentObserver(this);
+    }
+
+    @Override
+    public void onChange(boolean selfChange) {
+        handleValueChanged(getValue());
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImageView.java b/packages/SystemUI/src/com/android/systemui/qs/QSImageView.java
new file mode 100644
index 0000000..ed67560
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImageView.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.Path;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.VectorDrawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+/** ImageView that performs runtime modification of vector drawables (using FilterCanvas). **/
+public class QSImageView extends ImageView {
+
+    private final int mOutlineWidth;
+    private final int mColorEnabled;
+    private final int mColorDisabled;
+    private FilterCanvas mFilterCanvas;
+    private Canvas mCanvas;
+    private boolean mEnabledVersion = true;
+    private boolean mFilter;
+
+    public QSImageView(Context context) {
+        this(context, null);
+    }
+
+    public QSImageView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        final Resources res = context.getResources();
+        mOutlineWidth = res.getDimensionPixelSize(R.dimen.quick_settings_tile_icon_outline);
+        mColorEnabled = res.getColor(R.color.quick_settings_tile_icon_enabled);
+        mColorDisabled = res.getColor(R.color.quick_settings_tile_icon_disabled);
+    }
+
+    public void setEnabledVersion(boolean enabledVersion) {
+        mEnabledVersion = enabledVersion;
+        invalidate();
+    }
+
+    @Override
+    public void setImageDrawable(Drawable drawable) {
+        mFilter = drawable instanceof VectorDrawable;
+        super.setImageDrawable(drawable);
+    }
+
+    @Override
+    public void setImageResource(int resId) {
+        setImageDrawable(mContext.getDrawable(resId));
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mFilter) {
+            if (canvas != mCanvas) {
+                mCanvas = canvas;
+                mFilterCanvas = new QSFilterCanvas(canvas);
+            }
+            super.draw(mFilterCanvas);
+        } else {
+            super.draw(canvas);
+        }
+    }
+
+    private class QSFilterCanvas extends FilterCanvas {
+        public QSFilterCanvas(Canvas c) {
+            super(c);
+        }
+
+        @Override
+        public void drawPath(Path path, Paint paint) {
+            if (mEnabledVersion) {
+                paint.setColor(mColorEnabled);
+            } else {
+                paint.setStyle(Style.STROKE);
+                paint.setStrokeJoin(Paint.Join.ROUND);
+                paint.setColor(mColorDisabled);
+                paint.setStrokeWidth(mOutlineWidth);
+            }
+            super.drawPath(path, paint);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
new file mode 100644
index 0000000..afb5483
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import com.android.systemui.R;
+
+import java.util.ArrayList;
+
+/** View that represents the quick settings tile panel. **/
+public class QSPanel extends ViewGroup {
+    private static final float TILE_ASPECT = 1.4f;
+    private static final float LARGE_TILE_FACTOR = 1.1f;
+
+    private final Context mContext;
+    private final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>();
+    private final FrameLayout mDetail;
+    private final CircularClipper mClipper;
+    private final H mHandler = new H();
+
+    private int mColumns;
+    private int mCellWidth;
+    private int mCellHeight;
+    private int mLargeCellWidth;
+    private int mLargeCellHeight;
+
+    private TileRecord mDetailRecord;
+
+    public QSPanel(Context context) {
+        this(context, null);
+    }
+
+    public QSPanel(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+
+        mDetail = new FrameLayout(mContext);
+        mDetail.setVisibility(GONE);
+        mDetail.setClickable(true);
+        addView(mDetail);
+        mClipper = new CircularClipper(mDetail);
+        updateResources();
+    }
+
+    public void updateResources() {
+        final int columns = Math.max(1,
+                mContext.getResources().getInteger(R.integer.quick_settings_num_columns));
+        if (mColumns != columns) {
+            mColumns = columns;
+            postInvalidate();
+        }
+    }
+
+    public void setExpanded(boolean expanded) {
+        if (!expanded) {
+            showDetail(false /*show*/, mDetailRecord);
+        }
+        for (TileRecord r : mRecords) {
+            r.tile.setShown(expanded);
+        }
+    }
+
+    private void showDetail(boolean show, TileRecord r) {
+        mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0, r).sendToTarget();
+    }
+
+    private void setTileVisibility(View v, boolean visible) {
+        mHandler.obtainMessage(H.SET_TILE_VISIBILITY, visible ? 1 : 0, 0, v).sendToTarget();
+    }
+
+    private void handleSetTileVisibility(View v, boolean visible) {
+        v.setVisibility(visible ? VISIBLE : GONE);
+    }
+
+    public void addTile(final QSTile<?> tile) {
+        final TileRecord r = new TileRecord();
+        r.tile = tile;
+        r.tileView = tile.createTileView(mContext);
+        r.tileView.setVisibility(View.GONE);
+        r.tile.setCallback(new QSTile.Callback() {
+            @Override
+            public void onStateChanged(QSTile.State state) {
+                setTileVisibility(r.tileView, state.visible);
+                r.tileView.onStateChanged(state);
+            }
+            @Override
+            public void onShowDetail(boolean show) {
+                QSPanel.this.showDetail(show, r);
+            }
+        });
+        final View.OnClickListener click = new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                r.tile.click();
+            }
+        };
+        final View.OnClickListener clickSecondary = new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                r.tile.secondaryClick();
+            }
+        };
+        r.tileView.init(click, clickSecondary);
+        mRecords.add(r);
+
+        addView(r.tileView);
+    }
+
+    private void handleShowDetail(TileRecord r, boolean show) {
+        AnimatorListener listener = null;
+        if (show) {
+            if (mDetailRecord != null) return;
+            final View detail = r.tile.createDetailView(mContext, mDetail);
+            if (detail == null) return;
+            mDetailRecord = r;
+            mDetail.removeAllViews();
+            mDetail.bringToFront();
+            mDetail.addView(detail);
+        } else {
+            if (mDetailRecord == null) return;
+            listener = mTeardownDetailWhenDone;
+        }
+        int x = r.tileView.getLeft() + r.tileView.getWidth() / 2;
+        int y = r.tileView.getTop() + r.tileView.getHeight() / 2;
+        mClipper.animateCircularClip(x, y, show, listener);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final int width = MeasureSpec.getSize(widthMeasureSpec);
+        mCellWidth = width / mColumns;
+        mCellHeight = (int)(mCellWidth / TILE_ASPECT);
+        mLargeCellWidth = (int)(mCellWidth * LARGE_TILE_FACTOR);
+        mLargeCellHeight = (int)(mCellHeight * LARGE_TILE_FACTOR);
+        int r = 0;
+        int c = 0;
+        int rows = 0;
+        for (TileRecord record : mRecords) {
+            if (record.tileView.getVisibility() == GONE) continue;
+            record.row = r;
+            record.col = c;
+            rows = r + 1;
+            c++;
+            if (c == mColumns /*end of normal column*/ || r == 0 && c == 2 /*end of 1st column*/) {
+                c = 0;
+                r++;
+            }
+        }
+
+        for (TileRecord record : mRecords) {
+            if (record.tileView.getVisibility() == GONE) continue;
+            record.tileView.setDual(record.row == 0);
+            final int cw = record.row == 0 ? mLargeCellWidth : mCellWidth;
+            final int ch = record.row == 0 ? mLargeCellHeight : mCellHeight;
+            record.tileView.measure(exactly(cw), exactly(ch));
+        }
+        final int actualHeight = rows == 0 ? 0 : getRowTop(rows);
+        mDetail.measure(exactly(width), exactly(actualHeight));
+        setMeasuredDimension(width, actualHeight);
+    }
+
+    private static int exactly(int size) {
+        return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        final int w = mCellWidth * mColumns;
+        for (TileRecord record : mRecords) {
+            if (record.tileView.getVisibility() == GONE) continue;
+            final int cols = getColumnCount(record.row);
+            final int cw = record.row == 0 ? mLargeCellWidth : mCellWidth;
+            final int extra = (w - cw * cols) / (cols + 1);
+            final int left = record.col * cw + (record.col + 1) * extra;
+            final int top = getRowTop(record.row);
+            record.tileView.layout(left, top,
+                    left + record.tileView.getMeasuredWidth(),
+                    top + record.tileView.getMeasuredHeight());
+        }
+        mDetail.layout(0, 0, mDetail.getMeasuredWidth(), mDetail.getMeasuredHeight());
+    }
+
+    private int getRowTop(int row) {
+        if (row <= 0) return 0;
+        return mLargeCellHeight + (row - 1) * mCellHeight;
+    }
+
+    private int getColumnCount(int row) {
+        int cols = 0;
+        for (TileRecord record : mRecords) {
+            if (record.tileView.getVisibility() == GONE) continue;
+            if (record.row == row) cols++;
+        }
+        return cols;
+    }
+
+    private class H extends Handler {
+        private static final int SHOW_DETAIL = 1;
+        private static final int SET_TILE_VISIBILITY = 2;
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == SHOW_DETAIL) {
+                handleShowDetail((TileRecord)msg.obj, msg.arg1 != 0);
+            } else if (msg.what == SET_TILE_VISIBILITY) {
+                handleSetTileVisibility((View)msg.obj, msg.arg1 != 0);
+            }
+        }
+    }
+
+    private static final class TileRecord {
+        QSTile<?> tile;
+        QSTileView tileView;
+        int row;
+        int col;
+    }
+
+    private final AnimatorListenerAdapter mTeardownDetailWhenDone = new AnimatorListenerAdapter() {
+        public void onAnimationEnd(Animator animation) {
+            mDetail.removeAllViews();
+            mDetailRecord = null;
+        };
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
new file mode 100644
index 0000000..a2a7bc3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.VectorDrawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.systemui.qs.QSTile.State;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.Disposable;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.TetheringController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Base quick-settings tile, extend this to create a new tile.
+ *
+ * State management done on a looper provided by the host.  Tiles should update state in
+ * handleUpdateState.  Callbacks affecting state should use refreshState to trigger another
+ * state update pass on tile looper.
+ */
+public abstract class QSTile<TState extends State> implements Disposable {
+    private final String TAG = "QSTile." + getClass().getSimpleName();
+
+    protected final Host mHost;
+    protected final Context mContext;
+    protected final H mHandler;
+    protected final Handler mUiHandler = new Handler(Looper.getMainLooper());
+
+    private Callback mCallback;
+    protected final TState mState = newTileState();
+    private final TState mTmpState = newTileState();
+
+    abstract protected TState newTileState();
+    abstract protected void handleClick();
+    abstract protected void handleUpdateState(TState state, Object arg);
+
+    protected QSTile(Host host) {
+        mHost = host;
+        mContext = host.getContext();
+        mHandler = new H(host.getLooper());
+    }
+
+    public Host getHost() {
+        return mHost;
+    }
+
+    public QSTileView createTileView(Context context) {
+        return new QSTileView(context);
+    }
+
+    public View createDetailView(Context context, ViewGroup root) {
+        return null; // optional
+    }
+
+    // safe to call from any thread
+
+    public void setCallback(Callback callback) {
+        mHandler.obtainMessage(H.SET_CALLBACK, callback).sendToTarget();
+    }
+
+    public void click() {
+        mHandler.sendEmptyMessage(H.CLICK);
+    }
+
+    public void secondaryClick() {
+        mHandler.sendEmptyMessage(H.SECONDARY_CLICK);
+    }
+
+    public void showDetail(boolean show) {
+        mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0).sendToTarget();
+    }
+
+    protected final void refreshState() {
+        refreshState(null);
+    }
+
+    protected final void refreshState(Object arg) {
+        mHandler.obtainMessage(H.REFRESH_STATE, arg).sendToTarget();
+    }
+
+    public void userSwitch(int newUserId) {
+        mHandler.obtainMessage(H.USER_SWITCH, newUserId).sendToTarget();
+    }
+
+    public void setShown(boolean shown) {
+        mHandler.obtainMessage(H.SHOWN, shown ? 1 : 0, 0).sendToTarget();
+    }
+
+    // call only on tile worker looper
+
+    private void handleSetCallback(Callback callback) {
+        mCallback = callback;
+        handleRefreshState(null);
+    }
+
+    protected void handleSecondaryClick() {
+        // optional
+    }
+
+    protected void handleShown(boolean shown) {
+        // optional, discouraged
+    }
+
+    protected void handleRefreshState(Object arg) {
+        handleUpdateState(mTmpState, arg);
+        final boolean changed = mTmpState.copyTo(mState);
+        if (changed) {
+            handleStateChanged();
+        }
+    }
+
+    private void handleStateChanged() {
+        if (mCallback != null) {
+            mCallback.onStateChanged(mState);
+        }
+    }
+
+    private void handleShowDetail(boolean show) {
+        if (mCallback != null) {
+            mCallback.onShowDetail(show);
+        }
+    }
+
+    protected void handleUserSwitch(int newUserId) {
+        handleRefreshState(null);
+    }
+
+    protected final class H extends Handler {
+        private static final int SET_CALLBACK = 1;
+        private static final int CLICK = 2;
+        private static final int SECONDARY_CLICK = 3;
+        private static final int REFRESH_STATE = 4;
+        private static final int SHOW_DETAIL = 5;
+        private static final int USER_SWITCH = 6;
+        private static final int SHOWN = 7;
+
+        private H(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            String name = null;
+            try {
+                if (msg.what == SET_CALLBACK) {
+                    name = "handleSetCallback";
+                    handleSetCallback((QSTile.Callback)msg.obj);
+                } else if (msg.what == CLICK) {
+                    name = "handleClick";
+                    handleClick();
+                } else if (msg.what == SECONDARY_CLICK) {
+                    name = "handleSecondaryClick";
+                    handleSecondaryClick();
+                } else if (msg.what == REFRESH_STATE) {
+                    name = "handleRefreshState";
+                    handleRefreshState(msg.obj);
+                } else if (msg.what == SHOW_DETAIL) {
+                    name = "handleShowDetail";
+                    handleShowDetail(msg.arg1 != 0);
+                } else if (msg.what == USER_SWITCH) {
+                    name = "handleUserSwitch";
+                    handleUserSwitch(msg.arg1);
+                } else if (msg.what == SHOWN) {
+                    name = "handleShown";
+                    handleShown(msg.arg1 != 0);
+                }
+            } catch (Throwable t) {
+                final String error = "Error in " + name;
+                Log.w(TAG, error, t);
+                mHost.warn(error, t);
+            }
+        }
+    }
+
+    public interface Callback {
+        void onStateChanged(State state);
+        void onShowDetail(boolean show);
+    }
+
+    public interface Host {
+        void startSettingsActivity(Intent intent);
+        void warn(String message, Throwable t);
+        void collapsePanels();
+        Looper getLooper();
+        Context getContext();
+        VectorDrawable getVectorDrawable(int resId);
+        BluetoothController getBluetoothController();
+        LocationController getLocationController();
+        RotationLockController getRotationLockController();
+        List<QSTile<?>> getTiles();
+        NetworkController getNetworkController();
+        ZenModeController getZenModeController();
+        TetheringController getTetheringController();
+        CastController getCastController();
+    }
+
+    public static class State {
+        public boolean visible;
+        public int iconId;
+        public VectorDrawable icon;
+        public String label;
+        public String contentDescription;
+
+        public boolean copyTo(State other) {
+            if (other == null) throw new IllegalArgumentException();
+            if (!other.getClass().equals(getClass())) throw new IllegalArgumentException();
+            final boolean changed = other.visible != visible
+                    || other.iconId != iconId
+                    || !Objects.equals(other.icon, icon)
+                    || !Objects.equals(other.label, label)
+                    || !Objects.equals(other.contentDescription, contentDescription);
+            other.visible = visible;
+            other.iconId = iconId;
+            other.icon = icon;
+            other.label = label;
+            other.contentDescription = contentDescription;
+            return changed;
+        }
+
+        @Override
+        public String toString() {
+            return toStringBuilder().toString();
+        }
+
+        protected StringBuilder toStringBuilder() {
+            final StringBuilder sb = new StringBuilder(  getClass().getSimpleName()).append('[');
+            sb.append("visible=").append(visible);
+            sb.append(",iconId=").append(iconId);
+            sb.append(",icon=").append(icon);
+            sb.append(",label=").append(label);
+            sb.append(",contentDescription=").append(contentDescription);
+            return sb.append(']');
+        }
+    }
+
+    public static class BooleanState extends State {
+        public boolean value;
+
+        @Override
+        public boolean copyTo(State other) {
+            final BooleanState o = (BooleanState) other;
+            final boolean changed = super.copyTo(other) || o.value != value;
+            o.value = value;
+            return changed;
+        }
+
+        @Override
+        protected StringBuilder toStringBuilder() {
+            final StringBuilder rt = super.toStringBuilder();
+            rt.insert(rt.length() - 1, ",value=" + value);
+            return rt;
+        }
+    }
+
+    public static final class SignalState extends State {
+        public boolean enabled;
+        public boolean connected;
+        public boolean activityIn;
+        public boolean activityOut;
+        public int overlayIconId;
+
+        @Override
+        public boolean copyTo(State other) {
+            final SignalState o = (SignalState) other;
+            final boolean changed = o.enabled != enabled
+                    || o.connected != connected || o.activityIn != activityIn
+                    || o.activityOut != activityOut
+                    || o.overlayIconId != overlayIconId;
+            o.enabled = enabled;
+            o.connected = connected;
+            o.activityIn = activityIn;
+            o.activityOut = activityOut;
+            o.overlayIconId = overlayIconId;
+            return super.copyTo(other) || changed;
+        }
+
+        @Override
+        protected StringBuilder toStringBuilder() {
+            final StringBuilder rt = super.toStringBuilder();
+            rt.insert(rt.length() - 1, ",enabled=" + enabled);
+            rt.insert(rt.length() - 1, ",connected=" + connected);
+            rt.insert(rt.length() - 1, ",activityIn=" + activityIn);
+            rt.insert(rt.length() - 1, ",activityOut=" + activityOut);
+            rt.insert(rt.length() - 1, ",overlayIconId=" + overlayIconId);
+            return rt;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
new file mode 100644
index 0000000..17a95fb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView.ScaleType;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile.State;
+
+/** View that represents a standard quick settings tile. **/
+public class QSTileView extends ViewGroup {
+    private static final Typeface CONDENSED = Typeface.create("sans-serif-condensed",
+            Typeface.NORMAL);
+    private static final int VERTICAL_PADDING_FACTOR = 8;  // internal padding 1/8 the cell height
+
+    protected final Context mContext;
+    private final View mIcon;
+    private final View mDivider;
+    private final TextView mLabel;
+    private final H mHandler = new H();
+
+    private boolean mDual;
+    private OnClickListener mClickPrimary;
+    private OnClickListener mClickSecondary;
+
+    public QSTileView(Context context) {
+        super(context);
+
+        mContext = context;
+        final Resources res = context.getResources();
+        mLabel = new TextView(mContext);
+        mLabel.setId(android.R.id.title);
+        mLabel.setTextColor(res.getColor(R.color.quick_settings_tile_text));
+        mLabel.setGravity(Gravity.CENTER);
+        mLabel.setTypeface(CONDENSED);
+        mLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+                res.getDimensionPixelSize(R.dimen.quick_settings_tile_text_size));
+        addView(mLabel);
+        setClipChildren(false);
+
+        mIcon = createIcon();
+        addView(mIcon);
+
+        mDivider = new View(mContext);
+        mDivider.setBackgroundColor(res.getColor(R.color.quick_settings_tile_divider));
+        final int dh = res.getDimensionPixelSize(R.dimen.quick_settings_tile_divider_height);
+        mDivider.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, dh));
+        addView(mDivider);
+
+        setClickable(true);
+        setBackground(getSelectableBackground());
+    }
+
+    public void setDual(boolean dual) {
+        mDual = dual;
+        if (mDual) {
+            setOnClickListener(mClickPrimary);
+            mLabel.setClickable(true);
+            mLabel.setOnClickListener(mClickSecondary);
+        } else {
+            mLabel.setClickable(false);
+            setOnClickListener(mClickPrimary);
+        }
+        mDivider.setVisibility(dual ? VISIBLE : GONE);
+        postInvalidate();
+    }
+
+    public void init(OnClickListener clickPrimary, OnClickListener clickSecondary) {
+        mClickPrimary = clickPrimary;
+        mClickSecondary = clickSecondary;
+    }
+
+    protected View createIcon() {
+        QSImageView icon = new QSImageView(mContext);
+        icon.setId(android.R.id.icon);
+        icon.setScaleType(ScaleType.CENTER_INSIDE);
+        return icon;
+    }
+
+    private Drawable getSelectableBackground() {
+        final int[] attrs = new int[] { android.R.attr.selectableItemBackground};
+        final TypedArray ta = mContext.obtainStyledAttributes(attrs);
+        final Drawable d = ta.getDrawable(0);
+        ta.recycle();
+        return d;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final int w = MeasureSpec.getSize(widthMeasureSpec);
+        final int h = MeasureSpec.getSize(heightMeasureSpec);
+        final int p = h / VERTICAL_PADDING_FACTOR;
+        final int iconSpec = exactly((int)mLabel.getTextSize() * 2);
+        mIcon.measure(iconSpec, iconSpec);
+        mLabel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(h, MeasureSpec.AT_MOST));
+        mLabel.measure(widthMeasureSpec, exactly(mLabel.getMeasuredHeight() + p * 2));
+        if (mDual) {
+            mDivider.measure(widthMeasureSpec, exactly(mDivider.getLayoutParams().height));
+        }
+        setMeasuredDimension(w, h);
+    }
+
+    private static int exactly(int size) {
+        return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        final int w = getMeasuredWidth();
+        final int h = getMeasuredHeight();
+        final int p = h / VERTICAL_PADDING_FACTOR;
+        final int contentHeight = p + mIcon.getMeasuredHeight() + mLabel.getMeasuredHeight()
+                + (mDual ? (p + mDivider.getMeasuredHeight()) : 0);
+
+        int top = (h - contentHeight) / 2 + p;
+        final int iconLeft = (w - mIcon.getMeasuredWidth()) / 2;
+        layout(mIcon, iconLeft, top);
+        top = mIcon.getBottom();
+        if (mDual) {
+            top += p;
+            layout(mDivider, 0, top);
+            top = mDivider.getBottom();
+        }
+        layout(mLabel, 0, top);
+    }
+
+    private static void layout(View child, int left, int top) {
+        child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
+    }
+
+    protected void handleStateChanged(QSTile.State state) {
+        if (mIcon instanceof QSImageView) {
+            QSImageView qsiv = (QSImageView) mIcon;
+            if (state.icon != null) {
+                qsiv.setImageDrawable(state.icon);
+            } else if (state.iconId > 0) {
+                qsiv.setImageResource(state.iconId);
+            }
+            if (state.icon != null && state instanceof QSTile.BooleanState) {
+                qsiv.setEnabledVersion(((QSTile.BooleanState)state).value);
+            }
+        }
+        mLabel.setText(state.label);
+        setContentDescription(state.contentDescription);
+    }
+
+    public void onStateChanged(QSTile.State state) {
+        mHandler.obtainMessage(H.STATE_CHANGED, state).sendToTarget();
+    }
+
+    private class H extends Handler {
+        private static final int STATE_CHANGED = 1;
+        public H() {
+            super(Looper.getMainLooper());
+        }
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == STATE_CHANGED) {
+                handleStateChanged((State) msg.obj);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java b/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java
new file mode 100644
index 0000000..4debaa9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.Settings.Secure;
+
+import com.android.systemui.statusbar.policy.Disposable;
+
+/** Helper for managing a secure setting. **/
+public abstract class SecureSetting extends ContentObserver implements Disposable {
+    private final Context mContext;
+    private final String mSettingName;
+
+    protected abstract void handleValueChanged(int value);
+
+    public SecureSetting(Context context, Handler handler, String settingName) {
+        super(handler);
+        mContext = context;
+        mSettingName = settingName;
+        rebindForCurrentUser();
+    }
+
+    public void rebindForCurrentUser() {
+        mContext.getContentResolver().registerContentObserver(
+                Secure.getUriFor(mSettingName), false, this);
+    }
+
+    public int getValue() {
+        return Secure.getInt(mContext.getContentResolver(), mSettingName, 0);
+    }
+
+    public void setValue(int value) {
+        Secure.putInt(mContext.getContentResolver(), mSettingName, value);
+    }
+
+    @Override
+    public void dispose() {
+        mContext.getContentResolver().unregisterContentObserver(this);
+    }
+
+    @Override
+    public void onChange(boolean selfChange) {
+        handleValueChanged(getValue());
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
new file mode 100644
index 0000000..7b6c544
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile.SignalState;
+
+/** View that represents a custom quick settings tile for displaying signal info (wifi/cell). **/
+public final class SignalTileView extends QSTileView {
+    private static final long DEFAULT_DURATION = new ValueAnimator().getDuration();
+    private static final long SHORT_DURATION = DEFAULT_DURATION / 3;
+
+    private FrameLayout mIconFrame;
+    private ImageView mSignal;
+    private ImageView mOverlay;
+    private ImageView mIn;
+    private ImageView mOut;
+
+    public SignalTileView(Context context) {
+        super(context);
+
+        mIn = new ImageView(context);
+        mIn.setImageResource(R.drawable.ic_qs_signal_in);
+        addView(mIn);
+
+        mOut = new ImageView(context);
+        mOut.setImageResource(R.drawable.ic_qs_signal_out);
+        addView(mOut);
+    }
+
+    @Override
+    protected View createIcon() {
+        mIconFrame = new FrameLayout(mContext);
+        mSignal = new ImageView(mContext);
+        mIconFrame.addView(mSignal);
+        mOverlay = new ImageView(mContext);
+        mIconFrame.addView(mOverlay);
+        return mIconFrame;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        int hs = MeasureSpec.makeMeasureSpec(mIconFrame.getMeasuredHeight(), MeasureSpec.EXACTLY);
+        int ws = MeasureSpec.makeMeasureSpec(mIconFrame.getMeasuredHeight(), MeasureSpec.AT_MOST);
+        mIn.measure(ws, hs);
+        mOut.measure(ws, hs);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        layoutIndicator(mIn);
+        layoutIndicator(mOut);
+    }
+
+    private void layoutIndicator(View indicator) {
+        indicator.layout(
+                mIconFrame.getRight(),
+                mIconFrame.getBottom() - indicator.getMeasuredHeight(),
+                mIconFrame.getRight() + indicator.getMeasuredWidth(),
+                mIconFrame.getBottom());
+    }
+
+    @Override
+    protected void handleStateChanged(QSTile.State state) {
+        super.handleStateChanged(state);
+        final SignalState s = (SignalState) state;
+        mSignal.setImageDrawable(null);  // force refresh
+        mSignal.setImageResource(s.iconId);
+        if (s.overlayIconId > 0) {
+            mOverlay.setVisibility(VISIBLE);
+            mOverlay.setImageDrawable(null);  // force refresh
+            mOverlay.setImageResource(s.overlayIconId);
+        } else {
+            mOverlay.setVisibility(GONE);
+        }
+        setVisibility(mIn, s.activityIn);
+        setVisibility(mOut, s.activityOut);
+    }
+
+    private void setVisibility(View view, boolean visible) {
+        final float newAlpha = visible ? 1 : 0;
+        if (view.getAlpha() != newAlpha) {
+            view.animate()
+                .setDuration(visible ? SHORT_DURATION : DEFAULT_DURATION)
+                .alpha(newAlpha)
+                .withLayer()
+                .start();
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
new file mode 100644
index 0000000..5fe8422
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.provider.Settings.Global;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.GlobalSetting;
+import com.android.systemui.qs.QSTile;
+
+/** Quick settings tile: Airplane mode **/
+public class AirplaneModeTile extends QSTile<QSTile.BooleanState> {
+    private final GlobalSetting mSetting;
+
+    public AirplaneModeTile(Host host) {
+        super(host);
+
+        mSetting = new GlobalSetting(mContext, mHandler, Global.AIRPLANE_MODE_ON) {
+            @Override
+            protected void handleValueChanged(int value) {
+                handleRefreshState(value);
+            }
+        };
+
+        final IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        mContext.registerReceiver(mReceiver, filter);
+        refreshState();
+    }
+
+    @Override
+    protected BooleanState newTileState() {
+        return new BooleanState();
+    }
+
+    @Override
+    public void handleClick() {
+        setEnabled(!mState.value);
+    }
+
+    private void setEnabled(boolean enabled) {
+        mSetting.setValue(enabled ? 1 : 0);
+        final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        intent.putExtra("state", enabled);
+        mContext.sendBroadcast(intent);
+    }
+
+    @Override
+    protected void handleUpdateState(BooleanState state, Object arg) {
+        final int value = arg instanceof Integer ? (Integer)arg : mSetting.getValue();
+        final boolean airplaneMode = value != 0;
+        state.value = airplaneMode;
+        state.visible = true;
+        state.label = mContext.getString(R.string.quick_settings_airplane_mode_label);
+        state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_airplane);
+        if (airplaneMode) {
+            state.iconId =  R.drawable.ic_qs_airplane_on;
+            state.contentDescription =  mContext.getString(
+                    R.string.accessibility_quick_settings_airplane,
+                    mContext.getString(R.string.accessibility_desc_on));
+        } else {
+            state.iconId = R.drawable.ic_qs_airplane_off;
+            state.contentDescription =  mContext.getString(
+                    R.string.accessibility_quick_settings_airplane,
+                    mContext.getString(R.string.accessibility_desc_off));
+        }
+    }
+
+    public void dispose() {
+        mSetting.dispose();
+        mContext.unregisterReceiver(mReceiver);
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())) {
+                refreshState();
+            }
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
new file mode 100644
index 0000000..60a6047
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.bluetooth.BluetoothAdapter.BluetoothStateChangeCallback;
+import android.content.Intent;
+import android.provider.Settings;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.policy.BluetoothController;
+
+/** Quick settings tile: Bluetooth **/
+public class BluetoothTile extends QSTile<QSTile.BooleanState>  {
+    private static final Intent BLUETOOTH_SETTINGS = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
+
+    private final BluetoothController mController;
+
+    public BluetoothTile(Host host) {
+        super(host);
+        mController = host.getBluetoothController();
+        mController.addStateChangedCallback(mCallback);
+    }
+
+    @Override
+    protected BooleanState newTileState() {
+        return new BooleanState();
+    }
+
+    @Override
+    public void dispose() {
+        mController.removeStateChangedCallback(mCallback);
+    }
+
+    @Override
+    protected void handleClick() {
+        final boolean isEnabled = (Boolean)mState.value;
+        mController.setBluetoothEnabled(!isEnabled);
+    }
+
+    @Override
+    protected void handleSecondaryClick() {
+        mHost.startSettingsActivity(BLUETOOTH_SETTINGS);
+    }
+
+    @Override
+    protected void handleUpdateState(BooleanState state, Object arg) {
+        final boolean supported = mController.isBluetoothSupported();
+        final boolean enabled = mController.isBluetoothEnabled();
+        final boolean connected = mController.isBluetoothConnected();
+        state.visible = supported;
+        state.value = enabled;
+        state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_bluetooth);
+        final String stateContentDescription;
+        if (enabled) {
+            if (connected) {
+                state.iconId = R.drawable.ic_qs_bluetooth_on;
+                stateContentDescription = mContext.getString(R.string.accessibility_desc_connected);
+            } else {
+                state.iconId = R.drawable.ic_qs_bluetooth_not_connected;
+                stateContentDescription = mContext.getString(R.string.accessibility_desc_on);
+            }
+            state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
+        } else {
+            state.iconId = R.drawable.ic_qs_bluetooth_off;
+            state.label = mContext.getString(R.string.quick_settings_bluetooth_off_label);
+            stateContentDescription = mContext.getString(R.string.accessibility_desc_off);
+        }
+        state.contentDescription = mContext.getString(
+                R.string.accessibility_quick_settings_bluetooth, stateContentDescription);
+    }
+
+    private final BluetoothStateChangeCallback mCallback = new BluetoothStateChangeCallback() {
+        @Override
+        public void onBluetoothStateChange(boolean on) {
+            refreshState();
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java
new file mode 100644
index 0000000..0e9b9a7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.app.ActivityManagerNative;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.os.RemoteException;
+import android.provider.Settings.Global;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.GlobalSetting;
+import com.android.systemui.qs.QSTile;
+
+/** Quick settings tile: Bug report **/
+public class BugreportTile extends QSTile<QSTile.State> {
+
+    private final GlobalSetting mSetting;
+
+    public BugreportTile(Host host) {
+        super(host);
+        mSetting = new GlobalSetting(mContext, mHandler, Global.BUGREPORT_IN_POWER_MENU) {
+            @Override
+            protected void handleValueChanged(int value) {
+                handleRefreshState(null);
+            }
+        };
+    }
+
+    @Override
+    protected State newTileState() {
+        return new State();
+    }
+
+    @Override
+    public void dispose() {
+        mSetting.dispose();
+    }
+
+    @Override
+    protected void handleClick() {
+        mHost.collapsePanels();
+        mUiHandler.post(mShowDialog);
+    }
+
+    @Override
+    protected void handleUpdateState(State state, Object pushArg) {
+        state.visible = mSetting.getValue() != 0;
+        state.iconId = com.android.internal.R.drawable.stat_sys_adb;
+        state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_bugreport);
+        state.label = mContext.getString(com.android.internal.R.string.bugreport_title);
+    }
+
+    private final Runnable mShowDialog = new Runnable() {
+        @Override
+        public void run() {
+            final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+            builder.setPositiveButton(com.android.internal.R.string.report, new OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    if (which == DialogInterface.BUTTON_POSITIVE) {
+                        // Add a little delay before executing, to give the
+                        // dialog a chance to go away before it takes a
+                        // screenshot.
+                        mHandler.postDelayed(new Runnable() {
+                            @Override public void run() {
+                                try {
+                                    ActivityManagerNative.getDefault().requestBugReport();
+                                } catch (RemoteException e) {
+                                }
+                            }
+                        }, 500);
+                    }
+                }
+            });
+            builder.setMessage(com.android.internal.R.string.bugreport_message);
+            builder.setTitle(com.android.internal.R.string.bugreport_title);
+            builder.setCancelable(true);
+            final Dialog dialog = builder.create();
+            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+            try {
+                WindowManagerGlobal.getWindowManagerService().dismissKeyguard();
+            } catch (RemoteException e) {
+            }
+            dialog.show();
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
new file mode 100644
index 0000000..a3eaa2c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.app.Dialog;
+import android.content.Intent;
+import android.media.MediaRouter;
+import android.provider.Settings;
+import android.view.View;
+import android.view.WindowManager;
+
+import com.android.internal.app.MediaRouteDialogPresenter;
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.policy.CastController;
+
+/** Quick settings tile: Cast **/
+public class CastTile extends QSTile<QSTile.BooleanState> {
+    private static final Intent WIFI_DISPLAY_SETTINGS =
+            new Intent(Settings.ACTION_WIFI_DISPLAY_SETTINGS);
+
+    private final CastController mController;
+
+    private boolean mShown;
+
+    public CastTile(Host host) {
+        super(host);
+        mController = host.getCastController();
+        if (mController != null) {
+            mController.addCallback(mCallback);
+        }
+    }
+
+    @Override
+    protected BooleanState newTileState() {
+        return new BooleanState();
+    }
+
+    @Override
+    public void dispose() {
+        if (mController == null) return;
+        mController.removeCallback(mCallback);
+    }
+
+    @Override
+    protected void handleUserSwitch(int newUserId) {
+        super.handleUserSwitch(newUserId);
+        if (mController == null) return;
+        mController.setCurrentUserId(newUserId);
+    }
+
+    @Override
+    protected void handleShown(boolean shown) {
+        if (mShown == shown) return;
+        if (mController == null) return;
+        mShown = shown;
+        mController.setDiscovering(mShown);
+    }
+
+    @Override
+    protected void handleClick() {
+        mHost.collapsePanels();
+        mUiHandler.post(mShowDialog);
+    }
+
+    @Override
+    protected void handleUpdateState(BooleanState state, Object arg) {
+        state.visible = true;
+        state.label = mContext
+                .getString(R.string.quick_settings_remote_display_no_connection_label);
+        state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_cast);
+        if (arg instanceof CallbackInfo) {
+            final CallbackInfo cb = (CallbackInfo) arg;
+            if (cb.connectedRouteName != null) {
+                state.value = !cb.connecting;
+            }
+        }
+    }
+
+    private static class CallbackInfo {
+        boolean enabled;
+        boolean connecting;
+        String connectedRouteName;
+    }
+
+    private final CastController.Callback mCallback = new CastController.Callback() {
+        @Override
+        public void onStateChanged(boolean enabled, boolean connecting,
+                String connectedRouteName) {
+            final CallbackInfo info = new CallbackInfo();  // TODO pool
+            info.enabled = enabled;
+            info.connecting = connecting;
+            info.connectedRouteName = connectedRouteName;
+            refreshState(info);
+        }
+    };
+
+    private final Runnable mShowDialog = new Runnable() {
+        private Dialog mDialog;
+        @Override
+        public void run() {
+            mDialog = MediaRouteDialogPresenter.createDialog(mContext,
+                    MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
+                    new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    mDialog.dismiss();
+                    mHost.startSettingsActivity(WIFI_DISPLAY_SETTINGS);
+                }
+            });
+            mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
+            mDialog.show();
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
new file mode 100644
index 0000000..86a4e79
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.QSTileView;
+import com.android.systemui.qs.SignalTileView;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
+
+/** Quick settings tile: Cellular **/
+public class CellularTile extends QSTile<QSTile.SignalState> {
+    private static final Intent CELLULAR_SETTINGS = new Intent().setComponent(new ComponentName(
+            "com.android.settings", "com.android.settings.Settings$DataUsageSummaryActivity"));
+
+    private final NetworkController mController;
+
+    public CellularTile(Host host) {
+        super(host);
+        mController = host.getNetworkController();
+        mController.addNetworkSignalChangedCallback(mCallback);
+    }
+
+    @Override
+    protected SignalState newTileState() {
+        return new SignalState();
+    }
+
+    @Override
+    public void dispose() {
+        mController.removeNetworkSignalChangedCallback(mCallback);
+    }
+
+    @Override
+    public QSTileView createTileView(Context context) {
+        return new SignalTileView(context);
+    }
+
+    @Override
+    protected void handleClick() {
+        mHost.startSettingsActivity(CELLULAR_SETTINGS);
+    }
+
+    @Override
+    protected void handleUpdateState(SignalState state, Object arg) {
+        state.visible = mController.hasMobileDataFeature();
+        if (!state.visible) return;
+        final CallbackInfo cb = (CallbackInfo) arg;
+        if (cb == null) return;
+
+        final Resources r = mContext.getResources();
+        state.iconId = cb.enabled && (cb.mobileSignalIconId > 0)
+                ? cb.mobileSignalIconId
+                : R.drawable.ic_qs_signal_no_signal;
+        state.overlayIconId = cb.enabled && (cb.dataTypeIconId > 0) && !cb.wifiEnabled
+                ? cb.dataTypeIconId
+                : 0;
+        state.activityIn = cb.enabled && cb.activityIn;
+        state.activityOut = cb.enabled && cb.activityOut;
+
+        state.label = cb.enabled
+                ? removeTrailingPeriod(cb.enabledDesc)
+                : r.getString(R.string.quick_settings_rssi_emergency_only);
+
+        final String signalContentDesc = cb.enabled && (cb.mobileSignalIconId > 0)
+                ? cb.signalContentDescription
+                : r.getString(R.string.accessibility_no_signal);
+        final String dataContentDesc = cb.enabled && (cb.dataTypeIconId > 0) && !cb.wifiEnabled
+                ? cb.dataContentDescription
+                : r.getString(R.string.accessibility_no_data);
+        state.contentDescription = r.getString(
+                R.string.accessibility_quick_settings_mobile,
+                signalContentDesc, dataContentDesc,
+                state.label);
+    }
+
+    // Remove the period from the network name
+    public static String removeTrailingPeriod(String string) {
+        if (string == null) return null;
+        final int length = string.length();
+        if (string.endsWith(".")) {
+            return string.substring(0, length - 1);
+        }
+        return string;
+    }
+
+    private static final class CallbackInfo {
+        boolean enabled;
+        boolean wifiEnabled;
+        int mobileSignalIconId;
+        String signalContentDescription;
+        int dataTypeIconId;
+        String dataContentDescription;
+        boolean activityIn;
+        boolean activityOut;
+        String enabledDesc;
+    }
+
+    private final NetworkSignalChangedCallback mCallback = new NetworkSignalChangedCallback() {
+        private boolean mWifiEnabled;
+
+        @Override
+        public void onWifiSignalChanged(boolean enabled, int wifiSignalIconId,
+                boolean activityIn, boolean activityOut,
+                String wifiSignalContentDescriptionId, String description) {
+            mWifiEnabled = enabled;
+        }
+
+        @Override
+        public void onMobileDataSignalChanged(boolean enabled,
+                int mobileSignalIconId,
+                String mobileSignalContentDescriptionId, int dataTypeIconId,
+                boolean activityIn, boolean activityOut,
+                String dataTypeContentDescriptionId, String description) {
+            final CallbackInfo info = new CallbackInfo();  // TODO pool?
+            info.enabled = enabled;
+            info.wifiEnabled = mWifiEnabled;
+            info.mobileSignalIconId = mobileSignalIconId;
+            info.signalContentDescription = mobileSignalContentDescriptionId;
+            info.dataTypeIconId = dataTypeIconId;
+            info.dataContentDescription = dataTypeContentDescriptionId;
+            info.activityIn = activityIn;
+            info.activityOut = activityOut;
+            info.enabledDesc = description;
+            refreshState(info);
+        }
+
+        @Override
+        public void onAirplaneModeChanged(boolean enabled) {
+            // noop
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
new file mode 100644
index 0000000..66740af
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.provider.Settings.Secure;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.SecureSetting;
+
+/** Quick settings tile: Invert colors **/
+public class ColorInversionTile extends QSTile<QSTile.BooleanState> {
+
+    private final SecureSetting mSetting;
+
+    private boolean mVisible;
+
+    public ColorInversionTile(Host host) {
+        super(host);
+
+        mSetting = new SecureSetting(mContext, mHandler,
+                Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) {
+            @Override
+            protected void handleValueChanged(int value) {
+                handleRefreshState(value);
+            }
+        };
+
+        refreshState();
+    }
+
+    @Override
+    protected BooleanState newTileState() {
+        return new BooleanState();
+    }
+
+    @Override
+    public void dispose() {
+        mSetting.dispose();
+    }
+
+    @Override
+    protected void handleUserSwitch(int newUserId) {
+        mSetting.rebindForCurrentUser();
+    }
+
+    @Override
+    protected void handleClick() {
+        mSetting.setValue(mState.value ? 0 : 1);
+    }
+
+    @Override
+    protected void handleUpdateState(BooleanState state, Object arg) {
+        final int value = arg instanceof Integer ? (Integer) arg : mSetting.getValue();
+        final boolean enabled = value != 0;
+        if (enabled) {
+            mVisible = true;
+        }
+        state.visible = mVisible;
+        state.value = enabled;
+        state.label = mContext.getString(R.string.quick_settings_inversion_label);
+        state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_invert_colors);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
new file mode 100644
index 0000000..1a67afc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.content.Intent;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.policy.TetheringController;
+
+/** Quick settings tile: Hotspot **/
+public class HotspotTile extends QSTile<QSTile.State> {
+    private static final Intent TETHER_SETTINGS = new Intent()
+            .setClassName("com.android.settings", "com.android.settings.TetherSettings");
+
+    // TODO: implement. see com.android.settings.TetherSettings
+
+    private final TetheringController mController;
+
+    public HotspotTile(Host host) {
+        super(host);
+        mController = host.getTetheringController();
+    }
+
+    @Override
+    protected State newTileState() {
+        return new State();
+    }
+
+    @Override
+    public void dispose() {
+
+    }
+
+    @Override
+    protected void handleClick() {
+        mHost.startSettingsActivity(TETHER_SETTINGS);
+    }
+
+    @Override
+    protected void handleUpdateState(State state, Object arg) {
+        state.visible = mController != null;
+        state.label = mContext.getString(R.string.quick_settings_hotspot_label);
+        state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_hotspot);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
new file mode 100644
index 0000000..d32f98f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.LocationController.LocationSettingsChangeCallback;
+
+/** Quick settings tile: Location **/
+public class LocationTile extends QSTile<QSTile.BooleanState> {
+
+    private final LocationController mController;
+
+    public LocationTile(Host host) {
+        super(host);
+        mController = host.getLocationController();
+        mController.addSettingsChangedCallback(mCallback);
+    }
+
+    @Override
+    protected BooleanState newTileState() {
+        return new BooleanState();
+    }
+
+    public void dispose() {
+        mController.removeSettingsChangedCallback(mCallback);
+    }
+
+    @Override
+    protected void handleClick() {
+        final boolean wasEnabled = (Boolean) mState.value;
+        final boolean changed = mController.setLocationEnabled(!wasEnabled);
+        if (!wasEnabled && changed) {
+            // If we've successfully switched from location off to on, close the
+            // notifications tray to show the network location provider consent dialog.
+            mHost.collapsePanels();
+        }
+    }
+
+    @Override
+    protected void handleUpdateState(BooleanState state, Object arg) {
+        final boolean locationEnabled =  mController.isLocationEnabled();
+        state.visible = true;
+        state.value = locationEnabled;
+        state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_location);
+        if (locationEnabled) {
+            state.iconId = R.drawable.ic_qs_location_on;
+            state.label = mContext.getString(R.string.quick_settings_location_label);
+            state.contentDescription = mContext.getString(
+                    R.string.accessibility_quick_settings_location,
+                    mContext.getString(R.string.accessibility_desc_on));
+        } else {
+            state.iconId = R.drawable.ic_qs_location_off;
+            state.label = mContext.getString(R.string.quick_settings_location_off_label);
+            state.contentDescription = mContext.getString(
+                    R.string.accessibility_quick_settings_location,
+                    mContext.getString(R.string.accessibility_desc_off));
+        }
+    }
+
+    private final LocationSettingsChangeCallback mCallback = new LocationSettingsChangeCallback() {
+        @Override
+        public void onLocationSettingsChanged(boolean enabled) {
+            refreshState();
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java
new file mode 100644
index 0000000..36a579c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+
+/** Quick settings tile: Ringer mode **/
+public class RingerModeTile extends QSTile<RingerModeTile.IntState> {
+
+    private final AudioManager mAudioManager;
+
+    public RingerModeTile(Host host) {
+        super(host);
+        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        final IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
+        mContext.registerReceiver(mReceiver, filter);
+    }
+
+    @Override
+    protected IntState newTileState() {
+        return new IntState();
+    }
+
+    @Override
+    public void dispose() {
+        mContext.unregisterReceiver(mReceiver);
+    }
+
+    @Override
+    protected void handleClick() {
+        final int oldValue = (Integer) mState.value;
+        final int newValue =
+                oldValue == AudioManager.RINGER_MODE_NORMAL ? AudioManager.RINGER_MODE_VIBRATE
+              : oldValue == AudioManager.RINGER_MODE_VIBRATE ? AudioManager.RINGER_MODE_SILENT
+              : AudioManager.RINGER_MODE_NORMAL;
+
+        mAudioManager.setRingerMode(newValue);
+    }
+
+    @Override
+    protected void handleUpdateState(IntState state, Object arg) {
+        final int ringerMode = mAudioManager.getRingerMode();
+        state.visible = true;
+        state.value = ringerMode;
+        if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
+            state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_ringer_vibrate);
+            state.label = "Vibrate";
+        } else if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
+            state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_ringer_silent);
+            state.label = "Silent";
+        } else {
+            state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_ringer_audible);
+            state.label = "Audible";
+        }
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(intent.getAction())) {
+                refreshState();
+            }
+        }
+    };
+
+    public static class IntState extends QSTile.State {
+        public int value;
+
+        @Override
+        public boolean copyTo(State other) {
+            final IntState o = (IntState) other;
+            final boolean changed = o.value != value;
+            o.value = value;
+            return super.copyTo(other) || changed;
+        }
+
+        @Override
+        protected StringBuilder toStringBuilder() {
+            final StringBuilder rt = super.toStringBuilder();
+            rt.insert(rt.length() - 1, ",value=" + value);
+            return rt;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
new file mode 100644
index 0000000..1f00824
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.content.res.Configuration;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
+
+/** Quick settings tile: Rotation **/
+public class RotationLockTile extends QSTile<QSTile.BooleanState> {
+
+    private final RotationLockController mController;
+
+    public RotationLockTile(Host host) {
+        super(host);
+        mController = host.getRotationLockController();
+        if (mController == null) return;
+        mController.addRotationLockControllerCallback(mCallback);
+    }
+
+    @Override
+    protected BooleanState newTileState() {
+        return new BooleanState();
+    }
+
+    public void dispose() {
+        if (mController == null) return;
+        mController.removeRotationLockControllerCallback(mCallback);
+    }
+
+    @Override
+    protected void handleClick() {
+        if (mController == null) return;
+        mController.setRotationLocked(mState.value);
+    }
+
+    @Override
+    protected void handleUpdateState(BooleanState state, Object arg) {
+        if (mController == null) return;
+        final boolean rotationLocked = mController.isRotationLocked();
+        state.visible = mController.isRotationLockAffordanceVisible();
+        state.value = !rotationLocked;
+        if (rotationLocked) {
+            final int lockOrientation = mController.getRotationLockOrientation();
+            final int label = lockOrientation == Configuration.ORIENTATION_PORTRAIT
+                    ? R.string.quick_settings_rotation_locked_portrait_label
+                    : lockOrientation == Configuration.ORIENTATION_LANDSCAPE
+                    ? R.string.quick_settings_rotation_locked_landscape_label
+                    : R.string.quick_settings_rotation_locked_label;
+            state.iconId = R.drawable.ic_qs_rotation_lock;
+            state.label = mContext.getString(label);
+        } else {
+            state.iconId = R.drawable.ic_qs_rotation;
+            state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label);
+        }
+    }
+
+    private final RotationLockControllerCallback mCallback = new RotationLockControllerCallback() {
+        @Override
+        public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) {
+            refreshState();
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
new file mode 100644
index 0000000..e08a6fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.provider.Settings;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.QSTileView;
+import com.android.systemui.qs.SignalTileView;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
+
+/** Quick settings tile: Wifi **/
+public class WifiTile extends QSTile<QSTile.SignalState> {
+    private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS);
+
+    private final NetworkController mController;
+
+    public WifiTile(Host host) {
+        super(host);
+        mController = host.getNetworkController();
+        mController.addNetworkSignalChangedCallback(mCallback);
+    }
+
+    @Override
+    protected SignalState newTileState() {
+        return new SignalState();
+    }
+
+    @Override
+    public void dispose() {
+        mController.removeNetworkSignalChangedCallback(mCallback);
+    }
+
+    @Override
+    public QSTileView createTileView(Context context) {
+        return new SignalTileView(context);
+    }
+
+    @Override
+    protected void handleClick() {
+        mController.setWifiEnabled(!mState.enabled);
+    }
+
+    @Override
+    protected void handleSecondaryClick() {
+        mHost.startSettingsActivity(WIFI_SETTINGS);
+    }
+
+    @Override
+    protected void handleUpdateState(SignalState state, Object arg) {
+        if (arg == null) return;
+        state.visible = true;
+        CallbackInfo cb = (CallbackInfo) arg;
+
+        boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.enabledDesc != null);
+        boolean wifiNotConnected = (cb.wifiSignalIconId > 0) && (cb.enabledDesc == null);
+        state.enabled = wifiConnected;
+        state.connected = wifiConnected;
+        state.activityIn = cb.enabled && cb.activityIn;
+        state.activityOut = cb.enabled && cb.activityOut;
+        final String signalContentDescription;
+        final Resources r = mContext.getResources();
+        if (wifiConnected) {
+            state.iconId = cb.wifiSignalIconId;
+            state.label = removeDoubleQuotes(cb.enabledDesc);
+            signalContentDescription = cb.wifiSignalContentDescription;
+        } else if (wifiNotConnected) {
+            state.iconId = R.drawable.ic_qs_wifi_0;
+            state.label = r.getString(R.string.quick_settings_wifi_label);
+            signalContentDescription = r.getString(R.string.accessibility_no_wifi);
+        } else {
+            state.iconId = R.drawable.ic_qs_wifi_no_network;
+            state.label = r.getString(R.string.quick_settings_wifi_off_label);
+            signalContentDescription = r.getString(R.string.accessibility_wifi_off);
+        }
+        state.contentDescription = mContext.getString(
+                R.string.accessibility_quick_settings_wifi,
+                signalContentDescription,
+                state.connected ? state.label : "");
+    }
+
+    private static String removeDoubleQuotes(String string) {
+        if (string == null) return null;
+        final int length = string.length();
+        if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
+            return string.substring(1, length - 1);
+        }
+        return string;
+    }
+
+    private static final class CallbackInfo {
+        boolean enabled;
+        int wifiSignalIconId;
+        String enabledDesc;
+        boolean activityIn;
+        boolean activityOut;
+        String wifiSignalContentDescription;
+    }
+
+    private final NetworkSignalChangedCallback mCallback = new NetworkSignalChangedCallback() {
+        @Override
+        public void onWifiSignalChanged(boolean enabled, int wifiSignalIconId,
+                boolean activityIn, boolean activityOut,
+                String wifiSignalContentDescriptionId, String description) {
+            final CallbackInfo info = new CallbackInfo();
+            info.enabled = enabled;
+            info.wifiSignalIconId = wifiSignalIconId;
+            info.enabledDesc = description;
+            info.activityIn = activityIn;
+            info.activityOut = activityOut;
+            info.wifiSignalContentDescription = wifiSignalContentDescriptionId;
+            refreshState(info);
+        }
+
+        @Override
+        public void onMobileDataSignalChanged(boolean enabled,
+                int mobileSignalIconId,
+                String mobileSignalContentDescriptionId, int dataTypeIconId,
+                boolean activityIn, boolean activityOut,
+                String dataTypeContentDescriptionId, String description) {
+            // noop
+        }
+
+        @Override
+        public void onAirplaneModeChanged(boolean enabled) {
+            // noop
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeDetail.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeDetail.java
new file mode 100644
index 0000000..dceb856
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeDetail.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings;
+import android.service.notification.Condition;
+import android.util.AttributeSet;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ListView;
+import android.widget.RadioButton;
+import android.widget.RelativeLayout;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSImageView;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+import java.util.HashSet;
+
+/** Quick settings control panel: Zen mode **/
+public class ZenModeDetail extends RelativeLayout {
+    private static final String TAG = "ZenModeDetail";
+    private static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
+    private static final int[] MINUTES = new int[] { 15, 30, 45, 60, 120, 180, 240 };
+
+    private final H mHandler = new H();
+
+    private int mMinutesIndex = 3;
+    private Context mContext;
+    private ZenModeTile mTile;
+    private QSTile.Host mHost;
+    private ZenModeController mController;
+
+    private Switch mSwitch;
+    private ConditionAdapter mAdapter;
+
+    public ZenModeDetail(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void init(ZenModeTile tile) {
+        mTile = tile;
+        mHost = mTile.getHost();
+        mContext = getContext();
+        mController = mHost.getZenModeController();
+
+        final QSImageView close = (QSImageView) findViewById(android.R.id.button1);
+        close.setImageDrawable(mHost.getVectorDrawable(R.drawable.ic_qs_close));
+        close.setEnabledVersion(true);
+        close.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mTile.showDetail(false);
+            }
+        });
+        mSwitch = (Switch) findViewById(android.R.id.checkbox);
+        mSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                mController.setZen(isChecked);
+            }
+        });
+        mSwitch.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                final boolean isChecked = mSwitch.isChecked();
+                mController.setZen(isChecked);
+                if (!isChecked) {
+                    mTile.showDetail(false);
+                }
+            }
+        });
+
+        final View moreSettings = findViewById(android.R.id.button2);
+        moreSettings.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mHost.startSettingsActivity(ZEN_SETTINGS);
+                mTile.showDetail(false);
+            }
+        });
+        final ListView conditions = (ListView) findViewById(android.R.id.content);
+        mAdapter = new ConditionAdapter(mContext);
+        conditions.setAdapter(mAdapter);
+        mAdapter.add(updateTimeCondition());
+
+        updateZen(mController.isZen());
+    }
+
+    private Condition updateTimeCondition() {
+        final int minutes = MINUTES[mMinutesIndex];
+        final long millis = System.currentTimeMillis() + minutes * 60 * 1000;
+        final Uri id = new Uri.Builder().scheme(Condition.SCHEME).authority("android")
+                .appendPath("countdown").appendPath(Long.toString(millis)).build();
+        final int num = minutes < 60 ? minutes : minutes / 60;
+        final String units = minutes < 60 ? "minutes" : minutes == 60 ? "hour" : "hours";
+        return new Condition(id, "For " + num + " " + units, "", "", 0, Condition.STATE_TRUE,
+                Condition.FLAG_RELEVANT_NOW);
+    }
+
+    private void editTimeCondition(int delta) {
+        final int i = mMinutesIndex + delta;
+        if (i < 0 || i >= MINUTES.length) return;
+        mMinutesIndex = i;
+        mAdapter.remove(mAdapter.getItem(0));
+        final Condition c = updateTimeCondition();
+        mAdapter.insert(c, 0);
+        select(c);
+    }
+
+    private void select(Condition condition) {
+        mController.select(condition);
+    }
+
+    private void updateZen(boolean zen) {
+        mHandler.obtainMessage(H.UPDATE_ZEN, zen ? 1 : 0, 0).sendToTarget();
+    }
+
+    private void updateConditions(Condition[] conditions) {
+        if (conditions == null) return;
+        mHandler.obtainMessage(H.UPDATE_CONDITIONS, conditions).sendToTarget();
+    }
+
+    private void handleUpdateZen(boolean zen) {
+        mSwitch.setChecked(zen);
+    }
+
+    private void handleUpdateConditions(Condition[] conditions) {
+        for (int i = mAdapter.getCount() - 1; i > 0; i--) {
+            mAdapter.remove(mAdapter.getItem(i));
+        }
+        for (Condition condition : conditions) {
+            mAdapter.add(condition);
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mController.addCallback(mCallback);
+        mController.requestConditions(true);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mController.removeCallback(mCallback);
+        mController.requestConditions(false);
+    }
+
+    private final class H extends Handler {
+        private static final int UPDATE_ZEN = 1;
+        private static final int UPDATE_CONDITIONS = 2;
+
+        public H() {
+            super(Looper.getMainLooper());
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == UPDATE_ZEN) {
+                handleUpdateZen(msg.arg1 == 1);
+            } else if (msg.what == UPDATE_CONDITIONS) {
+                handleUpdateConditions((Condition[])msg.obj);
+            }
+        }
+    }
+
+    private final ZenModeController.Callback mCallback = new ZenModeController.Callback() {
+        @Override
+        public void onZenChanged(boolean zen) {
+            updateZen(zen);
+        }
+        public void onConditionsChanged(Condition[] conditions) {
+            updateConditions(conditions);
+        }
+    };
+
+    private final class ConditionAdapter extends ArrayAdapter<Condition> {
+        private final LayoutInflater mInflater;
+        private final HashSet<RadioButton> mRadioButtons = new HashSet<RadioButton>();
+
+        public ConditionAdapter(Context context) {
+            super(context, 0);
+            mInflater = LayoutInflater.from(new ContextThemeWrapper(context, R.style.QSWhiteTheme));
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            final Condition condition = getItem(position);
+            final boolean enabled = condition.state == Condition.STATE_TRUE;
+
+            final View row = convertView != null ? convertView : mInflater
+                    .inflate(R.layout.qs_zen_mode_detail_condition, parent, false);
+            final RadioButton rb = (RadioButton) row.findViewById(android.R.id.checkbox);
+            mRadioButtons.add(rb);
+            rb.setEnabled(enabled);
+            rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+                @Override
+                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                    if (isChecked) {
+                        for (RadioButton otherButton : mRadioButtons) {
+                            if (otherButton == rb) continue;
+                            otherButton.setChecked(false);
+                        }
+                        select(condition);
+                    }
+                }
+            });
+            final TextView title = (TextView) row.findViewById(android.R.id.title);
+            title.setText(condition.summary);
+            title.setEnabled(enabled);
+            title.setAlpha(enabled ? 1 : .5f);
+            final QSImageView button1 = (QSImageView) row.findViewById(android.R.id.button1);
+            button1.setImageDrawable(mHost.getVectorDrawable(R.drawable.ic_qs_minus));
+            button1.setEnabledVersion(true);
+            button1.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    rb.setChecked(true);
+                    editTimeCondition(-1);
+                }
+            });
+
+            final QSImageView button2 = (QSImageView) row.findViewById(android.R.id.button2);
+            button2.setImageDrawable(mHost.getVectorDrawable(R.drawable.ic_qs_plus));
+            button2.setEnabledVersion(true);
+            button2.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    rb.setChecked(true);
+                    editTimeCondition(1);
+                }
+            });
+            title.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    rb.setChecked(true);
+                }
+            });
+            if (position != 0) {
+                button1.setVisibility(View.GONE);
+                button2.setVisibility(View.GONE);
+            }
+            if (position == 0 && mRadioButtons.size() == 1) {
+                rb.setChecked(true);
+            }
+            return row;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java
new file mode 100644
index 0000000..83918e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.content.Context;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+/** Quick settings tile: Zen mode **/
+public class ZenModeTile extends QSTile<QSTile.BooleanState> {
+    private final ZenModeController mController;
+
+    public ZenModeTile(Host host) {
+        super(host);
+        mController = host.getZenModeController();
+        mController.addCallback(mCallback);
+    }
+
+    @Override
+    public View createDetailView(Context context, ViewGroup root) {
+        final Context themedContext = new ContextThemeWrapper(mContext, R.style.QSAccentTheme);
+        final ZenModeDetail v = (ZenModeDetail) LayoutInflater.from(themedContext)
+                .inflate(R.layout.qs_zen_mode_detail, root, false);
+        v.init(this);
+        return v;
+    }
+
+    @Override
+    protected BooleanState newTileState() {
+        return new BooleanState();
+    }
+
+    @Override
+    public void dispose() {
+        mController.removeCallback(mCallback);
+    }
+
+    @Override
+    protected void handleClick() {
+        final boolean newZen = !mState.value;
+        mController.setZen(newZen);
+        if (newZen) {
+            showDetail(true);
+        }
+    }
+
+    @Override
+    protected void handleUpdateState(BooleanState state, Object arg) {
+        final boolean zen = arg instanceof Boolean ? (Boolean)arg : mController.isZen();
+        state.value = zen;
+        state.visible = true;
+        state.iconId = R.drawable.stat_sys_zen_limited;
+        state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_zen);
+        state.label = mContext.getString(R.string.zen_mode_title);
+    }
+
+    private final ZenModeController.Callback mCallback = new ZenModeController.Callback() {
+        @Override
+        public void onZenChanged(boolean zen) {
+            refreshState(zen);
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index 1747e6e..c0a07e9 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -28,7 +28,6 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
 import android.widget.ImageView;
 
 import java.util.ArrayList;
@@ -36,6 +35,12 @@
 public class BrightnessController implements ToggleSlider.Listener {
     private static final String TAG = "StatusBar.BrightnessController";
 
+    /**
+     * {@link android.provider.Settings.System#SCREEN_AUTO_BRIGHTNESS_ADJ} uses the range [-1, 1].
+     * Using this factor, it is converted to [0, BRIGHTNESS_ADJ_RESOLUTION] for the SeekBar.
+     */
+    private static final float BRIGHTNESS_ADJ_RESOLUTION = 100;
+
     private final int mMinimumBacklight;
     private final int mMaximumBacklight;
 
@@ -51,6 +56,8 @@
     private ArrayList<BrightnessStateChangeCallback> mChangeCallbacks =
             new ArrayList<BrightnessStateChangeCallback>();
 
+    private boolean mAutomatic;
+
     public interface BrightnessStateChangeCallback {
         public void onBrightnessLevelChanged();
     }
@@ -62,6 +69,8 @@
                 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE);
         private final Uri BRIGHTNESS_URI =
                 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
+        private final Uri BRIGHTNESS_ADJ_URI =
+                Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ);
 
         public BrightnessObserver(Handler handler) {
             super(handler);
@@ -77,7 +86,10 @@
             if (selfChange) return;
             if (BRIGHTNESS_MODE_URI.equals(uri)) {
                 updateMode();
-            } else if (BRIGHTNESS_URI.equals(uri)) {
+                updateSlider();
+            } else if (BRIGHTNESS_URI.equals(uri) && !mAutomatic) {
+                updateSlider();
+            } else if (BRIGHTNESS_ADJ_URI.equals(uri) && mAutomatic) {
                 updateSlider();
             } else {
                 updateMode();
@@ -97,6 +109,9 @@
             cr.registerContentObserver(
                     BRIGHTNESS_URI,
                     false, this, UserHandle.USER_ALL);
+            cr.registerContentObserver(
+                    BRIGHTNESS_ADJ_URI,
+                    false, this, UserHandle.USER_ALL);
         }
 
         public void stopObserving() {
@@ -163,10 +178,8 @@
     }
 
     public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) {
-        setMode(automatic ? Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC
-                : Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
-        updateIcon(automatic);
-        if (!automatic) {
+        updateIcon(mAutomatic);
+        if (!mAutomatic) {
             final int val = value + mMinimumBacklight;
             setBrightness(val);
             if (!tracking) {
@@ -178,6 +191,18 @@
                         }
                     });
             }
+        } else {
+            final float adj = value / (BRIGHTNESS_ADJ_RESOLUTION / 2f) - 1;
+            setBrignessAdj(adj);
+            if (!tracking) {
+                AsyncTask.execute(new Runnable() {
+                    public void run() {
+                        Settings.System.putFloatForUser(mContext.getContentResolver(),
+                                Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, adj,
+                                UserHandle.USER_CURRENT);
+                    }
+                });
+            }
         }
 
         for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
@@ -198,6 +223,13 @@
         }
     }
 
+    private void setBrignessAdj(float adj) {
+        try {
+            mPower.setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(adj);
+        } catch (RemoteException ex) {
+        }
+    }
+
     private void updateIcon(boolean automatic) {
         if (mIcon != null) {
             mIcon.setImageResource(automatic ?
@@ -210,15 +242,12 @@
     private void updateMode() {
         if (mAutomaticAvailable) {
             int automatic;
-            try {
-                automatic = Settings.System.getIntForUser(mContext.getContentResolver(),
-                        Settings.System.SCREEN_BRIGHTNESS_MODE,
-                        UserHandle.USER_CURRENT);
-            } catch (SettingNotFoundException snfe) {
-                automatic = 0;
-            }
-            mControl.setChecked(automatic != 0);
-            updateIcon(automatic != 0);
+            automatic = Settings.System.getIntForUser(mContext.getContentResolver(),
+                    Settings.System.SCREEN_BRIGHTNESS_MODE,
+                    Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
+                    UserHandle.USER_CURRENT);
+            mAutomatic = automatic != 0;
+            updateIcon(mAutomatic);
         } else {
             mControl.setChecked(false);
             updateIcon(false /*automatic*/);
@@ -227,16 +256,20 @@
 
     /** Fetch the brightness from the system settings and update the slider */
     private void updateSlider() {
-        int value;
-        try {
-            value = Settings.System.getIntForUser(mContext.getContentResolver(),
-                    Settings.System.SCREEN_BRIGHTNESS,
+        if (mAutomatic) {
+            float value = Settings.System.getFloatForUser(mContext.getContentResolver(),
+                    Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0,
                     UserHandle.USER_CURRENT);
-        } catch (SettingNotFoundException ex) {
-            value = mMaximumBacklight;
+            mControl.setMax((int) BRIGHTNESS_ADJ_RESOLUTION);
+            mControl.setValue((int) ((value + 1) * BRIGHTNESS_ADJ_RESOLUTION / 2f));
+        } else {
+            int value;
+            value = Settings.System.getIntForUser(mContext.getContentResolver(),
+                    Settings.System.SCREEN_BRIGHTNESS, mMaximumBacklight,
+                    UserHandle.USER_CURRENT);
+            mControl.setMax(mMaximumBacklight - mMinimumBacklight);
+            mControl.setValue(value - mMinimumBacklight);
         }
-        mControl.setMax(mMaximumBacklight - mMinimumBacklight);
-        mControl.setValue(value - mMinimumBacklight);
     }
 
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
new file mode 100644
index 0000000..0606a94
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+/**
+ * Utility class to calculate general fling animation when the finger is released.
+ */
+public class FlingAnimationUtils {
+
+    private static final float LINEAR_OUT_SLOW_IN_Y2 = 0.35f;
+    private static final float MAX_LENGTH_SECONDS = 0.4f;
+    private static final float MIN_VELOCITY_DP_PER_SECOND = 250;
+
+    /**
+     * Crazy math. http://en.wikipedia.org/wiki/B%C3%A9zier_curve
+     */
+    private static final float LINEAR_OUT_SLOW_IN_START_GRADIENT = 1/LINEAR_OUT_SLOW_IN_Y2;
+
+    private Interpolator mLinearOutSlowIn;
+    private Interpolator mFastOutSlowIn;
+    private float mMinVelocityPxPerSecond;
+
+    public FlingAnimationUtils(Context ctx) {
+        mLinearOutSlowIn = new PathInterpolator(0, 0, LINEAR_OUT_SLOW_IN_Y2, 1);
+        mFastOutSlowIn
+                = AnimationUtils.loadInterpolator(ctx, android.R.interpolator.fast_out_slow_in);
+        mMinVelocityPxPerSecond
+                = MIN_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density;
+    }
+
+    /**
+     * Applies the interpolator and length to the animator, such that the fling animation is
+     * consistent with the finger motion.
+     *
+     * @param animator the animator to apply
+     * @param currValue the current value
+     * @param endValue the end value of the animator
+     * @param velocity the current velocity of the motion
+     */
+    public void apply(ValueAnimator animator, float currValue, float endValue, float velocity) {
+        float diff = Math.abs(endValue - currValue);
+        float velAbs = Math.abs(velocity);
+        float durationSeconds = LINEAR_OUT_SLOW_IN_START_GRADIENT * diff / velAbs;
+        if (durationSeconds <= MAX_LENGTH_SECONDS) {
+            animator.setInterpolator(mLinearOutSlowIn);
+        } else if (velAbs >= mMinVelocityPxPerSecond) {
+
+            // Cross fade between fast-out-slow-in and linear interpolator with current velocity.
+            durationSeconds = MAX_LENGTH_SECONDS;
+            VelocityInterpolator velocityInterpolator
+                    = new VelocityInterpolator(durationSeconds, velAbs, diff);
+            InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
+                    velocityInterpolator, mLinearOutSlowIn, mLinearOutSlowIn);
+            animator.setInterpolator(superInterpolator);
+        } else {
+
+            // Just use a normal interpolator which doesn't take the velocity into account.
+            durationSeconds = MAX_LENGTH_SECONDS;
+            animator.setInterpolator(mFastOutSlowIn);
+        }
+        animator.setDuration((long) (durationSeconds * 1000));
+    }
+
+    /**
+     * An interpolator which interpolates two interpolators with an interpolator.
+     */
+    private static final class InterpolatorInterpolator implements Interpolator {
+
+        private Interpolator mInterpolator1;
+        private Interpolator mInterpolator2;
+        private Interpolator mCrossfader;
+
+        InterpolatorInterpolator(Interpolator interpolator1, Interpolator interpolator2,
+                Interpolator crossfader) {
+            mInterpolator1 = interpolator1;
+            mInterpolator2 = interpolator2;
+            mCrossfader = crossfader;
+        }
+
+        @Override
+        public float getInterpolation(float input) {
+            float t = mCrossfader.getInterpolation(input);
+            return (1 - t) * mInterpolator1.getInterpolation(input)
+                    + t * mInterpolator2.getInterpolation(input);
+        }
+    }
+
+    /**
+     * An interpolator which interpolates with a fixed velocity.
+     */
+    private static final class VelocityInterpolator implements Interpolator {
+
+        private float mDurationSeconds;
+        private float mVelocity;
+        private float mDiff;
+
+        private VelocityInterpolator(float durationSeconds, float velocity, float diff) {
+            mDurationSeconds = durationSeconds;
+            mVelocity = velocity;
+            mDiff = diff;
+        }
+
+        @Override
+        public float getInterpolation(float input) {
+            float time = input * mDurationSeconds;
+            return time * mVelocity / mDiff;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 89da08f..2bc6f9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -28,19 +28,19 @@
 import android.widget.LinearLayout;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl;
 
 // Intimately tied to the design of res/layout/signal_cluster_view.xml
 public class SignalClusterView
         extends LinearLayout
-        implements NetworkController.SignalCluster {
+        implements NetworkControllerImpl.SignalCluster {
 
     static final boolean DEBUG = false;
     static final String TAG = "SignalClusterView";
     static final PorterDuffColorFilter PROBLEM_FILTER
             = new PorterDuffColorFilter(0xffab653b, PorterDuff.Mode.SRC_ATOP);
 
-    NetworkController mNC;
+    NetworkControllerImpl mNC;
 
     private boolean mWifiVisible = false;
     private int mWifiStrengthId = 0;
@@ -67,7 +67,7 @@
         super(context, attrs, defStyle);
     }
 
-    public void setNetworkController(NetworkController nc) {
+    public void setNetworkController(NetworkControllerImpl nc) {
         if (DEBUG) Log.d(TAG, "NetworkController=" + nc);
         mNC = nc;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index a3cf0f2..869edff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -27,7 +27,7 @@
 import com.android.systemui.DemoMode;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.LocationControllerImpl;
 
 public class DemoStatusIcons extends LinearLayout implements DemoMode {
     private final LinearLayout mStatusIcons;
@@ -74,9 +74,9 @@
             }
             String location = args.getString("location");
             if (location != null) {
-                int iconId = location.equals("show") ? LocationController.LOCATION_STATUS_ICON_ID
+                int iconId = location.equals("show") ? LocationControllerImpl.LOCATION_STATUS_ICON_ID
                         : 0;
-                updateSlot(LocationController.LOCATION_STATUS_ICON_PLACEHOLDER, null, iconId);
+                updateSlot(LocationControllerImpl.LOCATION_STATUS_ICON_PLACEHOLDER, null, iconId);
             }
             String alarm = args.getString("alarm");
             if (alarm != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 6132ed2..9054fe3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -32,6 +32,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -44,7 +45,7 @@
 
     PhoneStatusBar mStatusBar;
     private StatusBarHeaderView mHeader;
-    private QuickSettingsContainerView mQsContainer;
+    private View mQsContainer;
     private View mKeyguardStatusView;
     private ObservableScrollView mScrollView;
     private View mStackScrollerContainer;
@@ -53,8 +54,6 @@
     private int mNotificationTopPadding;
     private boolean mAnimateNextTopPaddingChange;
 
-    private Interpolator mExpansionInterpolator;
-
     private int mTrackingPointer;
     private VelocityTracker mVelocityTracker;
     private boolean mTracking;
@@ -78,6 +77,7 @@
     private int mStackScrollerIntrinsicPadding;
     private boolean mQsExpansionEnabled = true;
     private ValueAnimator mQsExpansionAnimator;
+    private FlingAnimationUtils mFlingAnimationUtils;
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -106,7 +106,7 @@
         mHeader.setOverlayParent(this);
         mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
         mStackScrollerContainer = findViewById(R.id.notification_container_parent);
-        mQsContainer = (QuickSettingsContainerView) findViewById(R.id.quick_settings_container);
+        mQsContainer = findViewById(R.id.quick_settings_container);
         mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view);
         mScrollView.setListener(this);
         mNotificationStackScroller = (NotificationStackScrollLayout)
@@ -115,8 +115,7 @@
         mNotificationTopPadding = getResources().getDimensionPixelSize(
                 R.dimen.notifications_top_padding);
         mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height);
-        mExpansionInterpolator = AnimationUtils.loadInterpolator(
-                getContext(), android.R.interpolator.fast_out_slow_in);
+        mFlingAnimationUtils = new FlingAnimationUtils(getContext());
     }
 
     @Override
@@ -250,6 +249,11 @@
 
             case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP:
+                trackMovement(event);
+                if (mTracking) {
+                    flingWithCurrentVelocity();
+                    mTracking = false;
+                }
                 mIntercepting = false;
                 break;
         }
@@ -265,6 +269,13 @@
         }
     }
 
+    private void flingWithCurrentVelocity() {
+        float vel = getCurrentVelocity();
+
+        // TODO: Better logic whether we should expand or not.
+        flingSettings(vel, vel > 0);
+    }
+
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         // TODO: Handle doublefinger swipe to notifications again. Look at history for a reference
@@ -314,12 +325,7 @@
                     mTracking = false;
                     mTrackingPointer = -1;
                     trackMovement(event);
-
-                    float vel = getCurrentVelocity();
-
-                    // TODO: Better logic whether we should expand or not.
-                    flingSettings(vel, vel > 0);
-
+                    flingWithCurrentVelocity();
                     if (mVelocityTracker != null) {
                         mVelocityTracker.recycle();
                         mVelocityTracker = null;
@@ -435,13 +441,9 @@
         }
     }
     private void flingSettings(float vel, boolean expand) {
-
-        // TODO: Actually use velocity.
-
         float target = expand ? mQsMaxExpansionHeight : mQsMinExpansionHeight;
         ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
-        animator.setDuration(EXPANSION_ANIMATION_LENGTH);
-        animator.setInterpolator(mExpansionInterpolator);
+        mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
         animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 4d09d6a..922ac05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -92,6 +92,8 @@
 import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
 import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.qs.QSPanel;
+import com.android.systemui.qs.QSTile;
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.DragDownHelper;
@@ -105,13 +107,15 @@
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
+import com.android.systemui.statusbar.policy.CastControllerImpl;
 import com.android.systemui.statusbar.policy.DateView;
 import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
-import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.RotationLockController;
 import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.LocationControllerImpl;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl;
+import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
+import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
 import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
@@ -180,12 +184,14 @@
     PhoneStatusBarPolicy mIconPolicy;
 
     // These are no longer handled by the policy, because we need custom strategies for them
-    BluetoothController mBluetoothController;
+    BluetoothControllerImpl mBluetoothController;
     BatteryController mBatteryController;
-    LocationController mLocationController;
-    NetworkController mNetworkController;
-    RotationLockController mRotationLockController;
+    LocationControllerImpl mLocationController;
+    NetworkControllerImpl mNetworkController;
+    RotationLockControllerImpl mRotationLockController;
     UserInfoController mUserInfoController;
+    ZenModeControllerImpl mZenModeController;
+    CastControllerImpl mCastController;
 
     int mNaturalBarHeight = -1;
     int mIconSize = -1;
@@ -226,9 +232,8 @@
     TextView mNotificationPanelDebugText;
 
     // settings
-    QuickSettings mQS;
     View mFlipSettingsView;
-    QuickSettingsContainerView mSettingsContainer;
+    private QSPanel mQSPanel;
 
     // top bar
     StatusBarHeaderView mHeader;
@@ -660,15 +665,16 @@
         setAreThereNotifications();
 
         // Other icons
-        mLocationController = new LocationController(mContext); // will post a notification
+        mLocationController = new LocationControllerImpl(mContext); // will post a notification
         mBatteryController = new BatteryController(mContext);
-        mNetworkController = new NetworkController(mContext);
-        mBluetoothController = new BluetoothController(mContext);
-        if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)
-                || QuickSettings.DEBUG_GONE_TILES) {
-            mRotationLockController = new RotationLockController(mContext);
+        mNetworkController = new NetworkControllerImpl(mContext);
+        mBluetoothController = new BluetoothControllerImpl(mContext);
+        if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) {
+            mRotationLockController = new RotationLockControllerImpl(mContext);
         }
         mUserInfoController = new UserInfoController(mContext);
+        mZenModeController = new ZenModeControllerImpl(mContext, mHandler);
+        mCastController = new CastControllerImpl(mContext);
         final SignalClusterView signalCluster =
                 (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
 
@@ -717,18 +723,17 @@
 //                    updateCarrierLabelVisibility(false);
         }
 
-        // Quick Settings needs a container to survive
-        mSettingsContainer = (QuickSettingsContainerView)
-                mStatusBarWindow.findViewById(R.id.quick_settings_container);
-        mFlipSettingsView = mSettingsContainer;
-        if (mSettingsContainer != null) {
-            mQS = new QuickSettings(mContext, mSettingsContainer);
-            mQS.setService(this);
-            mQS.setBar(mStatusBarView);
-            mQS.setup(mNetworkController, mBluetoothController, mBatteryController,
-                    mLocationController, mRotationLockController);
-        } else {
-            mQS = null; // fly away, be free
+        // Set up the quick settings tile panel
+        mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);
+        if (mQSPanel != null) {
+            final QSTileHost qsh = new QSTileHost(mContext, this,
+                    mBluetoothController, mLocationController, mRotationLockController,
+                    mNetworkController, mZenModeController, null /*tethering*/,
+                    mCastController);
+            for (QSTile<?> tile : qsh.getTiles()) {
+                mQSPanel.addTile(tile);
+            }
+            mHeader.setQSPanel(mQSPanel);
         }
 
         // User info. Trigger first load.
@@ -1471,7 +1476,7 @@
         }
     }
 
-    public Handler getHandler() {
+    private Handler getHandler() {
         return mHandler;
     }
 
@@ -1514,6 +1519,17 @@
         animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
     }
 
+    private final Runnable mAnimateCollapsePanels = new Runnable() {
+        @Override
+        public void run() {
+            animateCollapsePanels();
+        }
+    };
+
+    public void postAnimateCollapsePanels() {
+        mHandler.post(mAnimateCollapsePanels);
+    }
+
     public void animateCollapsePanels(int flags) {
         if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
             return;
@@ -1591,7 +1607,7 @@
     final int FLIP_DURATION_IN = 225;
     final int FLIP_DURATION = (FLIP_DURATION_IN + FLIP_DURATION_OUT);
 
-    Animator mScrollViewAnim, mFlipSettingsViewAnim, mClearButtonAnim;
+    Animator mScrollViewAnim, mClearButtonAnim;
 
     @Override
     public void animateExpandNotificationsPanel() {
@@ -1662,7 +1678,6 @@
         mStatusBarView.collapseAllPanels(/*animate=*/ false);
 
         // reset things to their proper state
-        if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
         if (mScrollViewAnim != null) mScrollViewAnim.cancel();
         if (mClearButtonAnim != null) mClearButtonAnim.cancel();
 
@@ -1984,7 +1999,6 @@
         }
 
         setNavigationIconHints(flags);
-        if (mQS != null) mQS.setImeWindowStatus(vis > 0);
     }
 
     @Override
@@ -2383,11 +2397,8 @@
      * meantime, just update the things that we know change.
      */
     void updateResources() {
-        final Context context = mContext;
-        final Resources res = context.getResources();
-
-        // Update the QuickSettings container
-        if (mQS != null) mQS.updateResources();
+        // Update the quick setting tiles
+        if (mQSPanel != null) mQSPanel.updateResources();
 
         loadDimens();
     }
@@ -2546,10 +2557,29 @@
                 || (mDisabled & StatusBarManager.DISABLE_SEARCH) != 0;
     }
 
-    public void startSettingsActivity(String action) {
-        if (mQS != null) {
-            mQS.startSettingsActivity(action);
+    public void postStartSettingsActivity(final Intent intent) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                handleStartSettingsActivity(intent, true /*onlyProvisioned*/);
+            }
+        });
+    }
+
+    private void handleStartSettingsActivity(Intent intent, boolean onlyProvisioned) {
+        if (onlyProvisioned && !isDeviceProvisioned()) return;
+        try {
+            // Dismiss the lock screen when Settings starts.
+            ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+        } catch (RemoteException e) {
         }
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+        animateCollapsePanels();
+    }
+
+    public void startSettingsActivity(String action) {
+        postStartSettingsActivity(new Intent(action));
     }
 
     private static class FastColorDrawable extends Drawable {
@@ -2704,18 +2734,20 @@
     private void updateKeyguardState() {
         if (mState == StatusBarState.KEYGUARD) {
             mKeyguardStatusView.setVisibility(View.VISIBLE);
-            mKeyguardBottomArea.setVisibility(View.VISIBLE);
             mKeyguardIndicationTextView.setVisibility(View.VISIBLE);
             mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
-
             mNotificationPanel.closeQs();
         } else {
             mKeyguardStatusView.setVisibility(View.GONE);
-            mKeyguardBottomArea.setVisibility(View.GONE);
             mKeyguardIndicationTextView.setVisibility(View.GONE);
         }
-        mSettingsContainer.setKeyguardShowing(mState == StatusBarState.KEYGUARD);
-        mHeader.setKeyguardShowing(mState == StatusBarState.KEYGUARD);
+        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
+            mKeyguardBottomArea.setVisibility(View.VISIBLE);
+            mHeader.setKeyguardShowing(true);
+        } else {
+            mKeyguardBottomArea.setVisibility(View.GONE);
+            mHeader.setKeyguardShowing(false);
+        }
 
         updateStackScrollerState();
         updatePublicMode();
@@ -2841,9 +2873,7 @@
     /**
      * If secure with redaction: Show bouncer, go to unlocked shade.
      *
-     * <p>If secure without redaction: Go to {@link StatusBarState#SHADE_LOCKED}.</p>
-     *
-     * <p>Otherwise go directly to unlocked shade.</p>
+     * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p>
      *
      * @param expandView The view to expand after going to the shade.
      */
@@ -2855,13 +2885,10 @@
         if (isLockscreenPublicMode() && !userAllowsPrivateNotificationsInPublic(mCurrentUserId)) {
             mLeaveOpenOnKeyguardHide = true;
             showBouncer();
-        } else if (mStatusBarKeyguardViewManager.isSecure()) {
+        } else {
             mNotificationPanel.animateNextTopPaddingChange();
             setBarState(StatusBarState.SHADE_LOCKED);
             updateKeyguardState();
-        } else {
-            mLeaveOpenOnKeyguardHide = true;
-            mStatusBarKeyguardViewManager.dismiss();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
new file mode 100644
index 0000000..1fe3be5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.VectorDrawable;
+import android.os.HandlerThread;
+import android.os.Looper;
+
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.tiles.AirplaneModeTile;
+import com.android.systemui.qs.tiles.BluetoothTile;
+import com.android.systemui.qs.tiles.BugreportTile;
+import com.android.systemui.qs.tiles.CastTile;
+import com.android.systemui.qs.tiles.CellularTile;
+import com.android.systemui.qs.tiles.ColorInversionTile;
+import com.android.systemui.qs.tiles.LocationTile;
+import com.android.systemui.qs.tiles.RingerModeTile;
+import com.android.systemui.qs.tiles.RotationLockTile;
+import com.android.systemui.qs.tiles.HotspotTile;
+import com.android.systemui.qs.tiles.WifiTile;
+import com.android.systemui.qs.tiles.ZenModeTile;
+import com.android.systemui.settings.CurrentUserTracker;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.TetheringController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Platform implementation of the quick settings tile host **/
+public class QSTileHost implements QSTile.Host {
+
+    private final Context mContext;
+    private final PhoneStatusBar mStatusBar;
+    private final BluetoothController mBluetooth;
+    private final LocationController mLocation;
+    private final RotationLockController mRotation;
+    private final NetworkController mNetwork;
+    private final ZenModeController mZen;
+    private final TetheringController mTethering;
+    private final CastController mCast;
+    private final Looper mLooper;
+    private final CurrentUserTracker mUserTracker;
+    private final ArrayList<QSTile<?>> mTiles = new ArrayList<QSTile<?>>();
+
+    public QSTileHost(Context context, PhoneStatusBar statusBar,
+            BluetoothController bluetooth, LocationController location,
+            RotationLockController rotation, NetworkController network,
+            ZenModeController zen, TetheringController tethering,
+            CastController cast) {
+        mContext = context;
+        mStatusBar = statusBar;
+        mBluetooth = bluetooth;
+        mLocation = location;
+        mRotation = rotation;
+        mNetwork = network;
+        mZen = zen;
+        mTethering = tethering;
+        mCast = cast;
+
+        final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName());
+        ht.start();
+        mLooper = ht.getLooper();
+
+        mTiles.add(new WifiTile(this));
+        mTiles.add(new BluetoothTile(this));
+        mTiles.add(new ColorInversionTile(this));
+        mTiles.add(new CellularTile(this));
+        mTiles.add(new AirplaneModeTile(this));
+        mTiles.add(new ZenModeTile(this));
+        mTiles.add(new RingerModeTile(this));
+        mTiles.add(new RotationLockTile(this));
+        mTiles.add(new LocationTile(this));
+        mTiles.add(new CastTile(this));
+        mTiles.add(new HotspotTile(this));
+        mTiles.add(new BugreportTile(this));
+
+        mUserTracker = new CurrentUserTracker(mContext) {
+            @Override
+            public void onUserSwitched(int newUserId) {
+                for (QSTile<?> tile : mTiles) {
+                    tile.userSwitch(newUserId);
+                }
+            }
+        };
+        mUserTracker.startTracking();
+    }
+
+    @Override
+    public List<QSTile<?>> getTiles() {
+        return mTiles;
+    }
+
+    @Override
+    public void startSettingsActivity(final Intent intent) {
+        mStatusBar.postStartSettingsActivity(intent);
+    }
+
+    @Override
+    public void warn(String message, Throwable t) {
+        // already logged
+    }
+
+    @Override
+    public void collapsePanels() {
+        mStatusBar.postAnimateCollapsePanels();
+    }
+
+    @Override
+    public Looper getLooper() {
+        return mLooper;
+    }
+
+    @Override
+    public Context getContext() {
+        return mContext;
+    }
+
+    @Override
+    public VectorDrawable getVectorDrawable(int resId) {
+        return (VectorDrawable) mContext.getDrawable(resId);
+    }
+
+    @Override
+    public BluetoothController getBluetoothController() {
+        return mBluetooth;
+    }
+
+    @Override
+    public LocationController getLocationController() {
+        return mLocation;
+    }
+
+    @Override
+    public RotationLockController getRotationLockController() {
+        return mRotation;
+    }
+
+    @Override
+    public NetworkController getNetworkController() {
+        return mNetwork;
+    }
+
+    @Override
+    public ZenModeController getZenModeController() {
+        return mZen;
+    }
+
+    @Override
+    public TetheringController getTetheringController() {
+        return mTethering;
+    }
+
+    @Override
+    public CastController getCastController() {
+        return mCast;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
deleted file mode 100644
index 8ce7279..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ /dev/null
@@ -1,1119 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.animation.ValueAnimator;
-import android.app.ActivityManagerNative;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.PendingIntent;
-import android.app.admin.DevicePolicyManager;
-import android.bluetooth.BluetoothAdapter;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.UserInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.hardware.display.DisplayManager;
-import android.media.MediaRouter;
-import android.net.wifi.WifiManager;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.AlarmClock;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.Profile;
-import android.provider.Settings;
-import android.security.KeyChain;
-import android.text.TextUtils.TruncateAt;
-import android.util.Log;
-import android.util.Pair;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
-import android.view.WindowManager.LayoutParams;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.internal.app.MediaRouteDialogPresenter;
-import com.android.systemui.R;
-import com.android.systemui.settings.UserSwitcherHostView;
-import com.android.systemui.statusbar.phone.QuickSettingsModel.ActivityState;
-import com.android.systemui.statusbar.phone.QuickSettingsModel.BluetoothState;
-import com.android.systemui.statusbar.phone.QuickSettingsModel.RSSIState;
-import com.android.systemui.statusbar.phone.QuickSettingsModel.State;
-import com.android.systemui.statusbar.phone.QuickSettingsModel.UserState;
-import com.android.systemui.statusbar.phone.QuickSettingsModel.WifiState;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.RotationLockController;
-
-import java.util.ArrayList;
-
-/**
- *
- */
-class QuickSettings {
-    static final boolean DEBUG_GONE_TILES = false;
-    private static final String TAG = "QuickSettings";
-    public static final boolean SHOW_IME_TILE = false;
-    public static final boolean SHOW_ACCESSIBILITY_TILES = true;
-
-    public static final boolean LONG_PRESS_TOGGLES = true;
-
-    private Context mContext;
-    private PanelBar mBar;
-    private QuickSettingsModel mModel;
-    private ViewGroup mContainerView;
-
-    private DevicePolicyManager mDevicePolicyManager;
-    private PhoneStatusBar mStatusBarService;
-    private BluetoothState mBluetoothState;
-    private BluetoothAdapter mBluetoothAdapter;
-    private WifiManager mWifiManager;
-
-    private BluetoothController mBluetoothController;
-    private RotationLockController mRotationLockController;
-    private LocationController mLocationController;
-
-    private AsyncTask<Void, Void, Pair<String, Drawable>> mUserInfoTask;
-    private AsyncTask<Void, Void, Pair<Boolean, Boolean>> mQueryCertTask;
-
-    boolean mTilesSetUp = false;
-    boolean mUseDefaultAvatar = false;
-
-    private Handler mHandler;
-
-    // The set of QuickSettingsTiles that have dynamic spans (and need to be updated on
-    // configuration change)
-    private final ArrayList<QuickSettingsTileView> mDynamicSpannedTiles =
-            new ArrayList<QuickSettingsTileView>();
-
-    public QuickSettings(Context context, QuickSettingsContainerView container) {
-        mDevicePolicyManager
-            = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
-        mContext = context;
-        mContainerView = container;
-        mModel = new QuickSettingsModel(context);
-        mBluetoothState = new QuickSettingsModel.BluetoothState();
-        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
-        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-
-        mHandler = new Handler();
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
-        filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
-        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
-        filter.addAction(Intent.ACTION_USER_SWITCHED);
-        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
-        filter.addAction(KeyChain.ACTION_STORAGE_CHANGED);
-        mContext.registerReceiver(mReceiver, filter);
-
-        IntentFilter profileFilter = new IntentFilter();
-        profileFilter.addAction(ContactsContract.Intents.ACTION_PROFILE_CHANGED);
-        profileFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
-        mContext.registerReceiverAsUser(mProfileReceiver, UserHandle.ALL, profileFilter,
-                null, null);
-    }
-
-    void setBar(PanelBar bar) {
-        mBar = bar;
-    }
-
-    public void setService(PhoneStatusBar phoneStatusBar) {
-        mStatusBarService = phoneStatusBar;
-    }
-
-    public PhoneStatusBar getService() {
-        return mStatusBarService;
-    }
-
-    public void setImeWindowStatus(boolean visible) {
-        mModel.onImeWindowStatusChanged(visible);
-    }
-
-    void setup(NetworkController networkController, BluetoothController bluetoothController,
-            BatteryController batteryController, LocationController locationController,
-            RotationLockController rotationLockController) {
-        mBluetoothController = bluetoothController;
-        mRotationLockController = rotationLockController;
-        mLocationController = locationController;
-
-        setupQuickSettings();
-        updateResources();
-        applyLocationEnabledStatus();
-
-        networkController.addNetworkSignalChangedCallback(mModel);
-        bluetoothController.addStateChangedCallback(mModel);
-        batteryController.addStateChangedCallback(mModel);
-        locationController.addSettingsChangedCallback(mModel);
-        if (rotationLockController != null) {
-            rotationLockController.addRotationLockControllerCallback(mModel);
-        }
-    }
-
-    private void queryForSslCaCerts() {
-        mQueryCertTask = new AsyncTask<Void, Void, Pair<Boolean, Boolean>>() {
-            @Override
-            protected Pair<Boolean, Boolean> doInBackground(Void... params) {
-                boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
-                boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
-
-                return Pair.create(hasCert, isManaged);
-            }
-            @Override
-            protected void onPostExecute(Pair<Boolean, Boolean> result) {
-                super.onPostExecute(result);
-                boolean hasCert = result.first;
-                boolean isManaged = result.second;
-                mModel.setSslCaCertWarningTileInfo(hasCert, isManaged);
-            }
-        };
-        mQueryCertTask.execute();
-    }
-
-    private void queryForUserInformation() {
-        Context currentUserContext = null;
-        UserInfo userInfo = null;
-        try {
-            userInfo = ActivityManagerNative.getDefault().getCurrentUser();
-            currentUserContext = mContext.createPackageContextAsUser("android", 0,
-                    new UserHandle(userInfo.id));
-        } catch (NameNotFoundException e) {
-            Log.e(TAG, "Couldn't create user context", e);
-            throw new RuntimeException(e);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Couldn't get user info", e);
-        }
-        final int userId = userInfo.id;
-        final String userName = userInfo.name;
-
-        final Context context = currentUserContext;
-        mUserInfoTask = new AsyncTask<Void, Void, Pair<String, Drawable>>() {
-            @Override
-            protected Pair<String, Drawable> doInBackground(Void... params) {
-                final UserManager um = UserManager.get(mContext);
-
-                // Fall back to the UserManager nickname if we can't read the name from the local
-                // profile below.
-                String name = userName;
-                Drawable avatar = null;
-                Bitmap rawAvatar = um.getUserIcon(userId);
-                if (rawAvatar != null) {
-                    avatar = new BitmapDrawable(mContext.getResources(), rawAvatar);
-                } else {
-                    avatar = mContext.getResources().getDrawable(R.drawable.ic_qs_default_user);
-                    mUseDefaultAvatar = true;
-                }
-
-                // If it's a single-user device, get the profile name, since the nickname is not
-                // usually valid
-                if (um.getUsers().size() <= 1) {
-                    // Try and read the display name from the local profile
-                    final Cursor cursor = context.getContentResolver().query(
-                            Profile.CONTENT_URI, new String[] {Phone._ID, Phone.DISPLAY_NAME},
-                            null, null, null);
-                    if (cursor != null) {
-                        try {
-                            if (cursor.moveToFirst()) {
-                                name = cursor.getString(cursor.getColumnIndex(Phone.DISPLAY_NAME));
-                            }
-                        } finally {
-                            cursor.close();
-                        }
-                    }
-                }
-                return new Pair<String, Drawable>(name, avatar);
-            }
-
-            @Override
-            protected void onPostExecute(Pair<String, Drawable> result) {
-                super.onPostExecute(result);
-                mModel.setUserTileInfo(result.first, result.second);
-                mUserInfoTask = null;
-            }
-        };
-        mUserInfoTask.execute();
-    }
-
-    private void setupQuickSettings() {
-        // Setup the tiles that we are going to be showing (including the temporary ones)
-        LayoutInflater inflater = LayoutInflater.from(mContext);
-
-        addUserTiles(mContainerView, inflater);
-        addSystemTiles(mContainerView, inflater);
-        addTemporaryTiles(mContainerView, inflater);
-        addAccessibilityTiles(mContainerView);
-
-        queryForUserInformation();
-        queryForSslCaCerts();
-        mTilesSetUp = true;
-    }
-
-    public void startSettingsActivity(String action) {
-        Intent intent = new Intent(action);
-        startSettingsActivity(intent);
-    }
-
-    private void startSettingsActivity(Intent intent) {
-        startSettingsActivity(intent, true);
-    }
-
-    private void collapsePanels() {
-        getService().animateCollapsePanels();
-    }
-
-    private void startSettingsActivity(Intent intent, boolean onlyProvisioned) {
-        if (onlyProvisioned && !getService().isDeviceProvisioned()) return;
-        try {
-            // Dismiss the lock screen when Settings starts.
-            ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
-        } catch (RemoteException e) {
-        }
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
-        mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
-        collapsePanels();
-    }
-
-    private void addAccessibilityTiles(ViewGroup parent) {
-        if (!DEBUG_GONE_TILES && !SHOW_ACCESSIBILITY_TILES) return;
-
-        // Color inversion tile
-        final SystemSettingTile inversionTile = new SystemSettingTile(mContext);
-        inversionTile.setUri(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
-                SystemSettingTile.TYPE_SECURE);
-        inversionTile.setFragment("Settings$AccessibilityInversionSettingsActivity");
-        mModel.addInversionTile(inversionTile, inversionTile.getRefreshCallback());
-        parent.addView(inversionTile);
-
-        // Color space adjustment tile
-        final SystemSettingTile colorSpaceTile = new SystemSettingTile(mContext);
-        colorSpaceTile.setUri(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
-                SystemSettingTile.TYPE_SECURE);
-        colorSpaceTile.setFragment("Settings$AccessibilityDaltonizerSettingsActivity");
-        mModel.addColorSpaceTile(colorSpaceTile, colorSpaceTile.getRefreshCallback());
-        parent.addView(colorSpaceTile);
-    }
-
-    private void addUserTiles(final ViewGroup parent, final LayoutInflater inflater) {
-        QuickSettingsTileView userTile = (QuickSettingsTileView)
-                inflater.inflate(R.layout.quick_settings_tile, parent, false);
-        userTile.setContent(R.layout.quick_settings_tile_user, inflater);
-        userTile.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                final UserManager um = UserManager.get(mContext);
-                if (um.isUserSwitcherEnabled()) {
-                    final ViewGroup switcherParent = getService().getQuickSettingsOverlayParent();
-                    final UserSwitcherHostView switcher = (UserSwitcherHostView) inflater.inflate(
-                            R.layout.user_switcher_host, switcherParent, false);
-                    switcher.setFinishRunnable(new Runnable() {
-                        @Override
-                        public void run() {
-                            switcherParent.removeView(switcher);
-                        }
-                    });
-                    switcher.refreshUsers();
-                    switcherParent.addView(switcher);
-                } else {
-                    collapsePanels();
-                    Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent(
-                            mContext, v, ContactsContract.Profile.CONTENT_URI,
-                            ContactsContract.QuickContact.MODE_LARGE, null);
-                    mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
-                }
-            }
-        });
-        mModel.addUserTile(userTile, new QuickSettingsModel.RefreshCallback() {
-            @Override
-            public void refreshView(QuickSettingsTileView view, State state) {
-                UserState us = (UserState) state;
-                ImageView iv = (ImageView) view.findViewById(R.id.user_imageview);
-                TextView tv = (TextView) view.findViewById(R.id.user_textview);
-                tv.setText(state.label);
-                iv.setImageDrawable(us.avatar);
-                view.setContentDescription(mContext.getString(
-                        R.string.accessibility_quick_settings_user, state.label));
-            }
-        });
-        parent.addView(userTile);
-        mDynamicSpannedTiles.add(userTile);
-
-        // Brightness
-        final QuickSettingsBasicTile brightnessTile
-                = new QuickSettingsBasicTile(mContext);
-        brightnessTile.setImageResource(R.drawable.ic_qs_brightness_auto_off);
-        brightnessTile.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                collapsePanels();
-                showBrightnessDialog();
-            }
-        });
-        mModel.addBrightnessTile(brightnessTile,
-                new QuickSettingsModel.BasicRefreshCallback(brightnessTile));
-        parent.addView(brightnessTile);
-        mDynamicSpannedTiles.add(brightnessTile);
-
-        // Settings tile
-        final QuickSettingsBasicTile settingsTile = new QuickSettingsBasicTile(mContext);
-        settingsTile.setImageResource(R.drawable.ic_qs_settings);
-        settingsTile.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                startSettingsActivity(android.provider.Settings.ACTION_SETTINGS);
-            }
-        });
-        mModel.addSettingsTile(settingsTile,
-                new QuickSettingsModel.BasicRefreshCallback(settingsTile));
-        parent.addView(settingsTile);
-        mDynamicSpannedTiles.add(settingsTile);
-    }
-
-    private void addSystemTiles(ViewGroup parent, LayoutInflater inflater) {
-        // Wi-fi
-        final QuickSettingsTileView wifiTile = (QuickSettingsTileView)
-                inflater.inflate(R.layout.quick_settings_tile, parent, false);
-        wifiTile.setContent(R.layout.quick_settings_tile_wifi, inflater);
-        wifiTile.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                startSettingsActivity(android.provider.Settings.ACTION_WIFI_SETTINGS);
-            }
-        });
-        if (LONG_PRESS_TOGGLES) {
-            wifiTile.setOnLongClickListener(new View.OnLongClickListener() {
-                @Override
-                public boolean onLongClick(View v) {
-                    final boolean enable =
-                            (mWifiManager.getWifiState() != WifiManager.WIFI_STATE_ENABLED);
-                    new AsyncTask<Void, Void, Void>() {
-                        @Override
-                        protected Void doInBackground(Void... args) {
-                            // Disable tethering if enabling Wifi
-                            final int wifiApState = mWifiManager.getWifiApState();
-                            if (enable && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) ||
-                                           (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) {
-                                mWifiManager.setWifiApEnabled(null, false);
-                            }
-
-                            mWifiManager.setWifiEnabled(enable);
-                            return null;
-                        }
-                    }.execute();
-                    wifiTile.setPressed(false);
-                    return true;
-                }} );
-        }
-        mModel.addWifiTile(wifiTile, new NetworkActivityCallback() {
-            @Override
-            public void refreshView(QuickSettingsTileView view, State state) {
-                WifiState wifiState = (WifiState) state;
-                ImageView iv = (ImageView) view.findViewById(R.id.image);
-                iv.setImageResource(wifiState.iconId);
-                setActivity(view, wifiState);
-                TextView tv = (TextView) view.findViewById(R.id.text);
-                tv.setText(wifiState.label);
-                wifiTile.setContentDescription(mContext.getString(
-                        R.string.accessibility_quick_settings_wifi,
-                        wifiState.signalContentDescription,
-                        (wifiState.connected) ? wifiState.label : ""));
-            }
-        });
-        parent.addView(wifiTile);
-
-        if (mModel.deviceHasMobileData()) {
-            // RSSI
-            QuickSettingsTileView rssiTile = (QuickSettingsTileView)
-                    inflater.inflate(R.layout.quick_settings_tile, parent, false);
-            rssiTile.setContent(R.layout.quick_settings_tile_rssi, inflater);
-            rssiTile.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    Intent intent = new Intent();
-                    intent.setComponent(new ComponentName(
-                            "com.android.settings",
-                            "com.android.settings.Settings$DataUsageSummaryActivity"));
-                    startSettingsActivity(intent);
-                }
-            });
-            mModel.addRSSITile(rssiTile, new NetworkActivityCallback() {
-                @Override
-                public void refreshView(QuickSettingsTileView view, State state) {
-                    RSSIState rssiState = (RSSIState) state;
-                    ImageView iv = (ImageView) view.findViewById(R.id.rssi_image);
-                    ImageView iov = (ImageView) view.findViewById(R.id.rssi_overlay_image);
-                    TextView tv = (TextView) view.findViewById(R.id.rssi_textview);
-                    // Force refresh
-                    iv.setImageDrawable(null);
-                    iv.setImageResource(rssiState.signalIconId);
-
-                    if (rssiState.dataTypeIconId > 0) {
-                        iov.setImageResource(rssiState.dataTypeIconId);
-                    } else {
-                        iov.setImageDrawable(null);
-                    }
-                    setActivity(view, rssiState);
-
-                    tv.setText(state.label);
-                    view.setContentDescription(mContext.getResources().getString(
-                            R.string.accessibility_quick_settings_mobile,
-                            rssiState.signalContentDescription, rssiState.dataContentDescription,
-                            state.label));
-                }
-            });
-            parent.addView(rssiTile);
-        }
-
-        // Rotation Lock
-        if (mRotationLockController != null) {
-            final QuickSettingsBasicTile rotationLockTile
-                    = new QuickSettingsBasicTile(mContext);
-            rotationLockTile.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View view) {
-                    final boolean locked = mRotationLockController.isRotationLocked();
-                    mRotationLockController.setRotationLocked(!locked);
-                }
-            });
-            mModel.addRotationLockTile(rotationLockTile, mRotationLockController,
-                    new QuickSettingsModel.RefreshCallback() {
-                        @Override
-                        public void refreshView(QuickSettingsTileView view, State state) {
-                            QuickSettingsModel.RotationLockState rotationLockState =
-                                    (QuickSettingsModel.RotationLockState) state;
-                            view.setVisibility(rotationLockState.visible
-                                    ? View.VISIBLE : View.GONE);
-                            if (state.iconId != 0) {
-                                // needed to flush any cached IDs
-                                rotationLockTile.setImageDrawable(null);
-                                rotationLockTile.setImageResource(state.iconId);
-                            }
-                            if (state.label != null) {
-                                rotationLockTile.setText(state.label);
-                            }
-                        }
-                    });
-            parent.addView(rotationLockTile);
-        }
-
-        // Battery
-        final QuickSettingsTileView batteryTile = (QuickSettingsTileView)
-                inflater.inflate(R.layout.quick_settings_tile, parent, false);
-        batteryTile.setContent(R.layout.quick_settings_tile_battery, inflater);
-        batteryTile.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                startSettingsActivity(Intent.ACTION_POWER_USAGE_SUMMARY);
-            }
-        });
-        mModel.addBatteryTile(batteryTile, new QuickSettingsModel.RefreshCallback() {
-            @Override
-            public void refreshView(QuickSettingsTileView unused, State state) {
-                QuickSettingsModel.BatteryState batteryState =
-                        (QuickSettingsModel.BatteryState) state;
-                String t;
-                if (batteryState.batteryLevel == 100) {
-                    t = mContext.getString(R.string.quick_settings_battery_charged_label);
-                } else {
-                    t = batteryState.pluggedIn
-                        ? mContext.getString(R.string.quick_settings_battery_charging_label,
-                                batteryState.batteryLevel)
-                        : mContext.getString(R.string.status_bar_settings_battery_meter_format,
-                                batteryState.batteryLevel);
-                }
-                ((TextView)batteryTile.findViewById(R.id.text)).setText(t);
-                batteryTile.setContentDescription(
-                        mContext.getString(R.string.accessibility_quick_settings_battery, t));
-            }
-        });
-        parent.addView(batteryTile);
-
-        // Bluetooth
-        if (mModel.deviceSupportsBluetooth()
-                || DEBUG_GONE_TILES) {
-            final QuickSettingsBasicTile bluetoothTile
-                    = new QuickSettingsBasicTile(mContext);
-            bluetoothTile.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    startSettingsActivity(android.provider.Settings.ACTION_BLUETOOTH_SETTINGS);
-                }
-            });
-            if (LONG_PRESS_TOGGLES) {
-                bluetoothTile.setOnLongClickListener(new View.OnLongClickListener() {
-                    @Override
-                    public boolean onLongClick(View v) {
-                        if (mBluetoothAdapter.isEnabled()) {
-                            mBluetoothAdapter.disable();
-                        } else {
-                            mBluetoothAdapter.enable();
-                        }
-                        bluetoothTile.setPressed(false);
-                        return true;
-                    }});
-            }
-            mModel.addBluetoothTile(bluetoothTile, new QuickSettingsModel.RefreshCallback() {
-                @Override
-                public void refreshView(QuickSettingsTileView unused, State state) {
-                    BluetoothState bluetoothState = (BluetoothState) state;
-                    bluetoothTile.setImageResource(state.iconId);
-
-                    /*
-                    Resources r = mContext.getResources();
-                    //TODO: Show connected bluetooth device label
-                    Set<BluetoothDevice> btDevices =
-                            mBluetoothController.getBondedBluetoothDevices();
-                    if (btDevices.size() == 1) {
-                        // Show the name of the bluetooth device you are connected to
-                        label = btDevices.iterator().next().getName();
-                    } else if (btDevices.size() > 1) {
-                        // Show a generic label about the number of bluetooth devices
-                        label = r.getString(R.string.quick_settings_bluetooth_multiple_devices_label,
-                                btDevices.size());
-                    }
-                    */
-                    bluetoothTile.setContentDescription(mContext.getString(
-                            R.string.accessibility_quick_settings_bluetooth,
-                            bluetoothState.stateContentDescription));
-                    bluetoothTile.setText(state.label);
-                }
-            });
-            parent.addView(bluetoothTile);
-        }
-
-        // Location
-        final QuickSettingsBasicTile locationTile
-                = new QuickSettingsBasicTile(mContext);
-        locationTile.setImageResource(R.drawable.ic_qs_location_on);
-        locationTile.setTextResource(R.string.quick_settings_location_label);
-        locationTile.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                startSettingsActivity(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
-            }
-        });
-        if (LONG_PRESS_TOGGLES) {
-            locationTile.setOnLongClickListener(new View.OnLongClickListener() {
-                @Override
-                public boolean onLongClick(View v) {
-                    boolean newLocationEnabledState = !mLocationController.isLocationEnabled();
-                    if (mLocationController.setLocationEnabled(newLocationEnabledState)
-                            && newLocationEnabledState) {
-                        // If we've successfully switched from location off to on, close the
-                        // notifications tray to show the network location provider consent dialog.
-                        Intent closeDialog = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-                        mContext.sendBroadcast(closeDialog);
-                    }
-                    return true; // Consume click
-                }} );
-        }
-        mModel.addLocationTile(locationTile, new QuickSettingsModel.RefreshCallback() {
-            @Override
-            public void refreshView(QuickSettingsTileView unused, State state) {
-                locationTile.setImageResource(state.iconId);
-                String locationState = mContext.getString(
-                        (state.enabled) ? R.string.accessibility_desc_on
-                                : R.string.accessibility_desc_off);
-                locationTile.setContentDescription(mContext.getString(
-                        R.string.accessibility_quick_settings_location,
-                        locationState));
-                locationTile.setText(state.label);
-            }
-        });
-        parent.addView(locationTile);
-
-        // Airplane Mode
-        final QuickSettingsBasicTile airplaneTile
-                = new QuickSettingsBasicTile(mContext);
-        mModel.addAirplaneModeTile(airplaneTile, new QuickSettingsModel.RefreshCallback() {
-            @Override
-            public void refreshView(QuickSettingsTileView unused, State state) {
-                airplaneTile.setImageResource(state.iconId);
-
-                String airplaneState = mContext.getString(
-                        (state.enabled) ? R.string.accessibility_desc_on
-                                : R.string.accessibility_desc_off);
-                airplaneTile.setContentDescription(
-                        mContext.getString(R.string.accessibility_quick_settings_airplane,
-                                airplaneState));
-                airplaneTile.setText(state.label);
-            }
-        });
-        parent.addView(airplaneTile);
-
-        // Zen Mode
-        final QuickSettingsBasicTile zenModeTile = new QuickSettingsBasicTile(mContext);
-        zenModeTile.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                showZenModeDialog();
-            }
-        });
-        mModel.addZenModeTile(zenModeTile, new QuickSettingsModel.RefreshCallback() {
-            @Override
-            public void refreshView(QuickSettingsTileView unused, State state) {
-                zenModeTile.setImageResource(state.iconId);
-                // TODO cut new assets
-                zenModeTile.getImageView().setAlpha(state.enabled ? 1 : .2f);
-                zenModeTile.getImageView().setScaleX(1.5f);
-                zenModeTile.getImageView().setScaleY(1.5f);
-                // for landscape version
-                zenModeTile.getTextView().setMaxLines(2);
-                zenModeTile.getTextView().setEllipsize(TruncateAt.END);
-                // TODO content description
-                zenModeTile.setText(state.label);
-            }
-        });
-        parent.addView(zenModeTile);
-    }
-
-    private void addTemporaryTiles(final ViewGroup parent, final LayoutInflater inflater) {
-        // Alarm tile
-        final QuickSettingsBasicTile alarmTile
-                = new QuickSettingsBasicTile(mContext);
-        alarmTile.setImageResource(R.drawable.ic_qs_alarm_on);
-        alarmTile.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                startSettingsActivity(AlarmClock.ACTION_SHOW_ALARMS);
-            }
-        });
-        mModel.addAlarmTile(alarmTile, new QuickSettingsModel.RefreshCallback() {
-            @Override
-            public void refreshView(QuickSettingsTileView unused, State alarmState) {
-                alarmTile.setText(alarmState.label);
-                alarmTile.setVisibility(alarmState.enabled ? View.VISIBLE : View.GONE);
-                alarmTile.setContentDescription(mContext.getString(
-                        R.string.accessibility_quick_settings_alarm, alarmState.label));
-            }
-        });
-        parent.addView(alarmTile);
-
-        // Remote Display
-        QuickSettingsBasicTile remoteDisplayTile
-                = new QuickSettingsBasicTile(mContext);
-        remoteDisplayTile.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                collapsePanels();
-
-                final Dialog[] dialog = new Dialog[1];
-                dialog[0] = MediaRouteDialogPresenter.createDialog(mContext,
-                        MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
-                        new View.OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        dialog[0].dismiss();
-                        startSettingsActivity(
-                                android.provider.Settings.ACTION_WIFI_DISPLAY_SETTINGS);
-                    }
-                });
-                dialog[0].getWindow().setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
-                dialog[0].show();
-            }
-        });
-        mModel.addRemoteDisplayTile(remoteDisplayTile,
-                new QuickSettingsModel.BasicRefreshCallback(remoteDisplayTile)
-                        .setShowWhenEnabled(true));
-        parent.addView(remoteDisplayTile);
-
-        if (SHOW_IME_TILE || DEBUG_GONE_TILES) {
-            // IME
-            final QuickSettingsBasicTile imeTile
-                    = new QuickSettingsBasicTile(mContext);
-            imeTile.setImageResource(R.drawable.ic_qs_ime);
-            imeTile.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    try {
-                        collapsePanels();
-                        Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER);
-                        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
-                        pendingIntent.send();
-                    } catch (Exception e) {}
-                }
-            });
-            mModel.addImeTile(imeTile,
-                    new QuickSettingsModel.BasicRefreshCallback(imeTile)
-                            .setShowWhenEnabled(true));
-            parent.addView(imeTile);
-        }
-
-        // Bug reports
-        final QuickSettingsBasicTile bugreportTile
-                = new QuickSettingsBasicTile(mContext);
-        bugreportTile.setImageResource(com.android.internal.R.drawable.stat_sys_adb);
-        bugreportTile.setTextResource(com.android.internal.R.string.bugreport_title);
-        bugreportTile.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                collapsePanels();
-                showBugreportDialog();
-            }
-        });
-        mModel.addBugreportTile(bugreportTile, new QuickSettingsModel.RefreshCallback() {
-            @Override
-            public void refreshView(QuickSettingsTileView view, State state) {
-                view.setVisibility(state.enabled ? View.VISIBLE : View.GONE);
-            }
-        });
-        parent.addView(bugreportTile);
-        /*
-        QuickSettingsTileView mediaTile = (QuickSettingsTileView)
-                inflater.inflate(R.layout.quick_settings_tile, parent, false);
-        mediaTile.setContent(R.layout.quick_settings_tile_media, inflater);
-        parent.addView(mediaTile);
-        QuickSettingsTileView imeTile = (QuickSettingsTileView)
-                inflater.inflate(R.layout.quick_settings_tile, parent, false);
-        imeTile.setContent(R.layout.quick_settings_tile_ime, inflater);
-        imeTile.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                parent.removeViewAt(0);
-            }
-        });
-        parent.addView(imeTile);
-        */
-
-        // SSL CA Cert Warning.
-        final QuickSettingsBasicTile sslCaCertWarningTile =
-                new QuickSettingsBasicTile(mContext, null, R.layout.quick_settings_tile_monitoring);
-        sslCaCertWarningTile.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                collapsePanels();
-                startSettingsActivity(Settings.ACTION_MONITORING_CERT_INFO);
-            }
-        });
-
-        sslCaCertWarningTile.setImageResource(
-                com.android.internal.R.drawable.indicator_input_error);
-        sslCaCertWarningTile.setTextResource(R.string.ssl_ca_cert_warning);
-
-        mModel.addSslCaCertWarningTile(sslCaCertWarningTile,
-                new QuickSettingsModel.BasicRefreshCallback(sslCaCertWarningTile)
-                        .setShowWhenEnabled(true));
-        parent.addView(sslCaCertWarningTile);
-    }
-
-    void updateResources() {
-        Resources r = mContext.getResources();
-
-        // Update the model
-        mModel.updateResources();
-
-        // Update the User, Time, and Settings tiles spans, and reset everything else
-        int span = r.getInteger(R.integer.quick_settings_user_time_settings_tile_span);
-        for (QuickSettingsTileView v : mDynamicSpannedTiles) {
-            v.setColumnSpan(span);
-        }
-        ((QuickSettingsContainerView)mContainerView).updateResources();
-        mContainerView.requestLayout();
-    }
-
-
-    private void showBrightnessDialog() {
-        Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG);
-        mContext.sendBroadcast(intent);
-    }
-
-    private void showBugreportDialog() {
-        final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
-        builder.setPositiveButton(com.android.internal.R.string.report, new OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                if (which == DialogInterface.BUTTON_POSITIVE) {
-                    // Add a little delay before executing, to give the
-                    // dialog a chance to go away before it takes a
-                    // screenshot.
-                    mHandler.postDelayed(new Runnable() {
-                        @Override public void run() {
-                            try {
-                                ActivityManagerNative.getDefault()
-                                        .requestBugReport();
-                            } catch (RemoteException e) {
-                            }
-                        }
-                    }, 500);
-                }
-            }
-        });
-        builder.setMessage(com.android.internal.R.string.bugreport_message);
-        builder.setTitle(com.android.internal.R.string.bugreport_title);
-        builder.setCancelable(true);
-        final Dialog dialog = builder.create();
-        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
-        try {
-            WindowManagerGlobal.getWindowManagerService().dismissKeyguard();
-        } catch (RemoteException e) {
-        }
-        dialog.show();
-    }
-
-    private void showZenModeDialog() {
-        final Dialog d = new Dialog(mContext);
-        d.requestWindowFeature(Window.FEATURE_NO_TITLE);
-        d.setCancelable(true);
-        d.setCanceledOnTouchOutside(true);
-        final ZenModeView v = new ZenModeView(mContext) {
-            @Override
-            protected void onConfigurationChanged(Configuration newConfig) {
-                super.onConfigurationChanged(newConfig);
-                WindowManager.LayoutParams lp = d.getWindow().getAttributes();
-                lp.width = mContext.getResources()
-                        .getDimensionPixelSize(R.dimen.zen_mode_dialog_width);
-                d.getWindow().setAttributes(lp);
-            }
-        };
-        v.setAutoActivate(true);
-        v.setAdapter(new ZenModeViewAdapter(mContext) {
-            @Override
-            public void configure() {
-                if (mStatusBarService != null) {
-                    mStatusBarService.startSettingsActivity(Settings.ACTION_ZEN_MODE_SETTINGS);
-                }
-                d.dismiss();
-            }
-            @Override
-            public void close() {
-                d.dismiss();
-            }
-        });
-        d.setContentView(v);
-        d.create();
-        d.getWindow().setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
-        WindowManager.LayoutParams lp = d.getWindow().getAttributes();
-        lp.width = mContext.getResources().getDimensionPixelSize(R.dimen.zen_mode_dialog_width);
-        d.getWindow().setAttributes(lp);
-        d.show();
-    }
-
-    private void applyBluetoothStatus() {
-        mModel.onBluetoothStateChange(mBluetoothState);
-    }
-
-    private void applyLocationEnabledStatus() {
-        mModel.onLocationSettingsChanged(mLocationController.isLocationEnabled());
-    }
-
-    void reloadUserInfo() {
-        if (mUserInfoTask != null) {
-            mUserInfoTask.cancel(false);
-            mUserInfoTask = null;
-        }
-        if (mTilesSetUp) {
-            queryForUserInformation();
-            queryForSslCaCerts();
-        }
-    }
-
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
-                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
-                        BluetoothAdapter.ERROR);
-                mBluetoothState.enabled = (state == BluetoothAdapter.STATE_ON);
-                applyBluetoothStatus();
-            } else if (BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
-                int status = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
-                        BluetoothAdapter.STATE_DISCONNECTED);
-                mBluetoothState.connected = (status == BluetoothAdapter.STATE_CONNECTED);
-                applyBluetoothStatus();
-            } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                reloadUserInfo();
-            } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
-                if (mUseDefaultAvatar) {
-                    queryForUserInformation();
-                }
-            } else if (KeyChain.ACTION_STORAGE_CHANGED.equals(action)) {
-                queryForSslCaCerts();
-            }
-        }
-    };
-
-    private final BroadcastReceiver mProfileReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (ContactsContract.Intents.ACTION_PROFILE_CHANGED.equals(action) ||
-                    Intent.ACTION_USER_INFO_CHANGED.equals(action)) {
-                try {
-                    final int currentUser = ActivityManagerNative.getDefault().getCurrentUser().id;
-                    final int changedUser =
-                            intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId());
-                    if (changedUser == currentUser) {
-                        reloadUserInfo();
-                    }
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Couldn't get current user id for profile change", e);
-                }
-            }
-
-        }
-    };
-
-    private abstract static class NetworkActivityCallback
-            implements QuickSettingsModel.RefreshCallback {
-        private final long mDefaultDuration = new ValueAnimator().getDuration();
-        private final long mShortDuration = mDefaultDuration / 3;
-
-        public void setActivity(View view, ActivityState state) {
-            setVisibility(view.findViewById(R.id.activity_in), state.activityIn);
-            setVisibility(view.findViewById(R.id.activity_out), state.activityOut);
-        }
-
-        private void setVisibility(View view, boolean visible) {
-            final float newAlpha = visible ? 1 : 0;
-            if (view.getAlpha() != newAlpha) {
-                view.animate()
-                    .setDuration(visible ? mShortDuration : mDefaultDuration)
-                    .alpha(newAlpha)
-                    .start();
-            }
-        }
-    }
-
-    /**
-     * Quick Setting tile that represents a secure setting. This type of tile
-     * can toggle a URI within Settings.Secure on click and launch a Settings
-     * fragment on long-click.
-     */
-    public class SystemSettingTile extends QuickSettingsBasicTile {
-        private static final int TYPE_GLOBAL = 0;
-        private static final int TYPE_SECURE = 1;
-        private static final int TYPE_SYSTEM = 2;
-
-        private final QuickSettingsModel.BasicRefreshCallback mRefreshCallback;
-
-        private String mFragment;
-        private String mName;
-        private int mType;
-
-        public SystemSettingTile(Context context) {
-            super(context);
-
-            mRefreshCallback = new QuickSettingsModel.BasicRefreshCallback(this);
-            mRefreshCallback.setShowWhenEnabled(true);
-        }
-
-        @Override
-        public boolean performLongClick() {
-            if (mFragment != null) {
-                collapsePanels();
-
-                final Intent intent = new Intent();
-                intent.setComponent(new ComponentName(
-                        "com.android.settings", "com.android.settings." + mFragment));
-                startSettingsActivity(intent);
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public boolean performClick() {
-            if (mName != null) {
-                collapsePanels();
-
-                final ContentResolver cr = mContext.getContentResolver();
-                switch (mType) {
-                    case TYPE_GLOBAL: {
-                        final boolean enable = Settings.Global.getInt(cr, mName, 0) == 0;
-                        Settings.Global.putInt(cr, mName, enable ? 1 : 0);
-                    } break;
-                    case TYPE_SECURE: {
-                        final boolean enable = Settings.Secure.getIntForUser(
-                                cr, mName, 0, UserHandle.USER_CURRENT) == 0;
-                        Settings.Secure.putIntForUser(
-                                cr, mName, enable ? 1 : 0, UserHandle.USER_CURRENT);
-                    } break;
-                    case TYPE_SYSTEM: {
-                        final boolean enable = Settings.System.getIntForUser(
-                                cr, mName, 0, UserHandle.USER_CURRENT) == 0;
-                        Settings.System.putIntForUser(
-                                cr, mName, enable ? 1 : 0, UserHandle.USER_CURRENT);
-                    } break;
-                }
-                return true;
-            }
-            return false;
-        }
-
-        /**
-         * Specifies the fragment within the com.android.settings package to
-         * launch when this tile is long-clicked.
-         *
-         * @param fragment a fragment name within the com.android.settings
-         *            package
-         */
-        public void setFragment(String fragment) {
-            mFragment = fragment;
-            setLongClickable(fragment != null);
-        }
-
-        /**
-         * Specifies the setting name and type to toggle when this tile is
-         * clicked.
-         *
-         * @param name a setting name
-         * @param type the type of setting, one of:
-         *            <ul>
-         *            <li>{@link #TYPE_GLOBAL}
-         *            <li>{@link #TYPE_SECURE}
-         *            <li>{@link #TYPE_SYSTEM}
-         *            </ul>
-         */
-        public void setUri(String name, int type) {
-            mName = name;
-            mType = type;
-            setClickable(mName != null);
-        }
-
-        /**
-         * @return the refresh callback for this tile
-         */
-        public QuickSettingsModel.BasicRefreshCallback getRefreshCallback() {
-            return mRefreshCallback;
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsBasicTile.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsBasicTile.java
deleted file mode 100644
index 099780c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsBasicTile.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-
-class QuickSettingsBasicTile extends QuickSettingsTileView {
-    private final TextView mTextView;
-    private final ImageView mImageView;
-
-    public QuickSettingsBasicTile(Context context) {
-        this(context, null);
-    }
-
-    public QuickSettingsBasicTile(Context context, AttributeSet attrs) {
-        this(context, attrs, R.layout.quick_settings_tile_basic);
-    }
-
-    public QuickSettingsBasicTile(Context context, AttributeSet attrs, int layoutId) {
-        super(context, attrs);
-
-        setLayoutParams(new FrameLayout.LayoutParams(
-            FrameLayout.LayoutParams.MATCH_PARENT,
-            context.getResources().getDimensionPixelSize(R.dimen.quick_settings_cell_height)
-        ));
-        setBackgroundResource(R.drawable.qs_tile_background);
-        addView(LayoutInflater.from(context).inflate(layoutId, null),
-                new FrameLayout.LayoutParams(
-                        FrameLayout.LayoutParams.MATCH_PARENT,
-                        FrameLayout.LayoutParams.MATCH_PARENT));
-        mTextView = (TextView) findViewById(R.id.text);
-        mImageView = (ImageView) findViewById(R.id.image);
-    }
-
-    @Override
-    void setContent(int layoutId, LayoutInflater inflater) {
-        throw new RuntimeException("why?");
-    }
-
-    public ImageView getImageView() {
-        return mImageView;
-    }
-
-    public TextView getTextView() {
-        return mTextView;
-    }
-
-    public void setImageDrawable(Drawable drawable) {
-        mImageView.setImageDrawable(drawable);
-    }
-
-    public void setImageResource(int resId) {
-        mImageView.setImageResource(resId);
-    }
-
-    public void setText(CharSequence text) {
-        mTextView.setText(text);
-    }
-
-    public void setTextResource(int resId) {
-        mTextView.setText(resId);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsContainerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsContainerView.java
deleted file mode 100644
index c44cb0c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsContainerView.java
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.animation.LayoutTransition;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import com.android.systemui.R;
-
-/**
- *
- */
-class QuickSettingsContainerView extends FrameLayout {
-
-    private static boolean sShowScrim = true;
-
-    private final Context mContext;
-
-    // The number of columns in the QuickSettings grid
-    private int mNumColumns;
-
-    private boolean mKeyguardShowing;
-    private int mMaxRows;
-    private int mMaxRowsOnKeyguard;
-
-    // The gap between tiles in the QuickSettings grid
-    private float mCellGap;
-
-    private ScrimView mScrim;
-
-    public QuickSettingsContainerView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mContext = context;
-        updateResources();
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        if (sShowScrim) {
-            mScrim = new ScrimView(mContext);
-            addView(mScrim);
-        }
-        // TODO: Setup the layout transitions
-        LayoutTransition transitions = getLayoutTransition();
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        if (mScrim != null) {
-            sShowScrim = false;
-            removeView(mScrim);
-        }
-        return super.onTouchEvent(event);
-    }
-
-    void updateResources() {
-        Resources r = getContext().getResources();
-        mCellGap = r.getDimension(R.dimen.quick_settings_cell_gap);
-        mNumColumns = r.getInteger(R.integer.quick_settings_num_columns);
-        mMaxRows = r.getInteger(R.integer.quick_settings_max_rows);
-        mMaxRowsOnKeyguard = r.getInteger(R.integer.quick_settings_max_rows_keyguard);
-        requestLayout();
-    }
-
-    void setKeyguardShowing(boolean showing) {
-        mKeyguardShowing = showing;
-        requestLayout();
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        // Calculate the cell width dynamically
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-        int height = MeasureSpec.getSize(heightMeasureSpec);
-        int availableWidth = (int) (width - getPaddingLeft() - getPaddingRight() -
-                (mNumColumns - 1) * mCellGap);
-        float cellWidth = (float) Math.ceil(((float) availableWidth) / mNumColumns);
-
-        // Update each of the children's widths accordingly to the cell width
-        final int N = getChildCount();
-        int cellHeight = 0;
-        int cursor = 0;
-        int maxRows = mKeyguardShowing ? mMaxRowsOnKeyguard : mMaxRows;
-
-        for (int i = 0; i < N; ++i) {
-            if (getChildAt(i).equals(mScrim)) {
-                continue;
-            }
-            // Update the child's width
-            QuickSettingsTileView v = (QuickSettingsTileView) getChildAt(i);
-            if (v.getVisibility() != View.GONE) {
-                int row = (int) (cursor / mNumColumns);
-                if (row >= maxRows) continue;
-
-                ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
-                int colSpan = v.getColumnSpan();
-                lp.width = (int) ((colSpan * cellWidth) + (colSpan - 1) * mCellGap);
-
-                // Measure the child
-                int newWidthSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
-                int newHeightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
-                v.measure(newWidthSpec, newHeightSpec);
-
-                // Save the cell height
-                if (cellHeight <= 0) {
-                    cellHeight = v.getMeasuredHeight();
-                }
-                cursor += colSpan;
-            }
-        }
-
-        // Set the measured dimensions.  We always fill the tray width, but wrap to the height of
-        // all the tiles.
-        int numRows = (int) Math.ceil((float) cursor / mNumColumns);
-        int newHeight = (int) ((numRows * cellHeight) + ((numRows - 1) * mCellGap)) +
-                getPaddingTop() + getPaddingBottom();
-        setMeasuredDimension(width, newHeight);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        if (mScrim != null) {
-            mScrim.bringToFront();
-        }
-        final int N = getChildCount();
-        final boolean isLayoutRtl = isLayoutRtl();
-        final int width = getWidth();
-
-        int x = getPaddingStart();
-        int y = getPaddingTop();
-        int cursor = 0;
-        int maxRows = mKeyguardShowing ? mMaxRowsOnKeyguard : mMaxRows;
-
-        for (int i = 0; i < N; ++i) {
-            if (getChildAt(i).equals(mScrim)) {
-                int w = right - left - getPaddingLeft() - getPaddingRight();
-                int h = bottom - top - getPaddingTop() - getPaddingBottom();
-                mScrim.measure(
-                        MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY));
-                mScrim.layout(getPaddingLeft(), getPaddingTop(), right, bottom);
-                continue;
-            }
-            QuickSettingsTileView child = (QuickSettingsTileView) getChildAt(i);
-            ViewGroup.LayoutParams lp = child.getLayoutParams();
-            if (child.getVisibility() != GONE) {
-                final int col = cursor % mNumColumns;
-                final int colSpan = child.getColumnSpan();
-
-                final int childWidth = lp.width;
-                final int childHeight = lp.height;
-
-                int row = (int) (cursor / mNumColumns);
-                if (row >= maxRows) continue;
-
-                // Push the item to the next row if it can't fit on this one
-                if ((col + colSpan) > mNumColumns) {
-                    x = getPaddingStart();
-                    y += childHeight + mCellGap;
-                    row++;
-                }
-
-                final int childLeft = (isLayoutRtl) ? width - x - childWidth : x;
-                final int childRight = childLeft + childWidth;
-
-                final int childTop = y;
-                final int childBottom = childTop + childHeight;
-
-                // Layout the container
-                child.layout(childLeft, childTop, childRight, childBottom);
-
-                // Offset the position by the cell gap or reset the position and cursor when we
-                // reach the end of the row
-                cursor += child.getColumnSpan();
-                if (cursor < (((row + 1) * mNumColumns))) {
-                    x += childWidth + mCellGap;
-                } else {
-                    x = getPaddingStart();
-                    y += childHeight + mCellGap;
-                }
-            }
-        }
-    }
-
-    private static final class ScrimView extends View {
-        private static final int SCRIM = 0x4f000000;
-        private static final int COLOR = 0xaf4285f4;
-
-        private final Paint mLinePaint;
-        private final int mStrokeWidth;
-        private final Rect mTmp = new Rect();
-        private final Paint mTextPaint;
-        private final int mTextSize;
-
-        public ScrimView(Context context) {
-            super(context);
-            setFocusable(false);
-            final Resources res = context.getResources();
-            mStrokeWidth = res.getDimensionPixelSize(R.dimen.quick_settings_tmp_scrim_stroke_width);
-            mTextSize = res.getDimensionPixelSize(R.dimen.quick_settings_tmp_scrim_text_size);
-
-            mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-            mLinePaint.setColor(COLOR);
-            mLinePaint.setStrokeWidth(mStrokeWidth);
-            mLinePaint.setStrokeJoin(Paint.Join.ROUND);
-            mLinePaint.setStrokeCap(Paint.Cap.ROUND);
-            mLinePaint.setStyle(Paint.Style.STROKE);
-
-            mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-            mTextPaint.setColor(COLOR);
-            mTextPaint.setTextSize(mTextSize);
-            mTextPaint.setTypeface(Typeface.create("sans-serif-condensed", Typeface.BOLD));
-        }
-
-        @Override
-        protected void onDraw(Canvas canvas) {
-            final int w = getMeasuredWidth();
-            final int h = getMeasuredHeight();
-            final int f = mStrokeWidth * 3 / 4;
-
-            canvas.drawColor(SCRIM);
-            canvas.drawPath(line(f, h / 2, w - f, h / 2), mLinePaint);
-            canvas.drawPath(line(w / 2, f, w / 2, h - f), mLinePaint);
-
-            final int s = mStrokeWidth;
-            mTextPaint.setTextAlign(Paint.Align.RIGHT);
-            canvas.drawText("FUTURE", w / 2 - s, h / 2 - s, mTextPaint);
-            mTextPaint.setTextAlign(Paint.Align.LEFT);
-            canvas.drawText("SITE OF", w / 2 + s, h / 2 - s , mTextPaint);
-            mTextPaint.setTextAlign(Paint.Align.RIGHT);
-            drawUnder(canvas, "QUANTUM", w / 2 - s, h / 2 + s);
-            mTextPaint.setTextAlign(Paint.Align.LEFT);
-            drawUnder(canvas, "SETTINGS", w / 2 + s, h / 2 + s);
-        }
-
-        private void drawUnder(Canvas c, String text, float x, float y) {
-            if (mTmp.isEmpty()) {
-                mTextPaint.getTextBounds(text, 0, text.length(), mTmp);
-            }
-            c.drawText(text, x, y + mTmp.height() * .85f, mTextPaint);
-        }
-
-        private Path line(float x1, float y1, float x2, float y2) {
-            final int a = mStrokeWidth * 2;
-            final Path p = new Path();
-            p.moveTo(x1, y1);
-            p.lineTo(x2, y2);
-            if (y1 == y2) {
-                p.moveTo(x1 + a, y1 + a);
-                p.lineTo(x1, y1);
-                p.lineTo(x1 + a, y1 - a);
-
-                p.moveTo(x2 - a, y2 - a);
-                p.lineTo(x2, y2);
-                p.lineTo(x2 - a, y2 + a);
-            }
-            if (x1 == x2) {
-                p.moveTo(x1 - a, y1 + a);
-                p.lineTo(x1, y1);
-                p.lineTo(x1 + a, y1 + a);
-
-                p.moveTo(x2 - a, y2 - a);
-                p.lineTo(x2, y2);
-                p.lineTo(x2 + a, y2 - a);
-            }
-            return p;
-        }
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
deleted file mode 100644
index 9b90d3d..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ /dev/null
@@ -1,1047 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothAdapter.BluetoothStateChangeCallback;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.graphics.drawable.Drawable;
-import android.media.MediaRouter;
-import android.media.MediaRouter.RouteInfo;
-import android.net.ConnectivityManager;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.systemui.R;
-import com.android.systemui.settings.BrightnessController.BrightnessStateChangeCallback;
-import com.android.systemui.settings.CurrentUserTracker;
-import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
-import com.android.systemui.statusbar.policy.LocationController.LocationSettingsChangeCallback;
-import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
-import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
-
-import java.util.List;
-
-class QuickSettingsModel implements BluetoothStateChangeCallback,
-        NetworkSignalChangedCallback,
-        BatteryStateChangeCallback,
-        BrightnessStateChangeCallback,
-        RotationLockControllerCallback,
-        LocationSettingsChangeCallback {
-    // Sett InputMethoManagerService
-    private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
-
-    /** Represents the state of a given attribute. */
-    static class State {
-        int iconId;
-        String label;
-        boolean enabled = false;
-    }
-    static class BatteryState extends State {
-        int batteryLevel;
-        boolean pluggedIn;
-    }
-    static class ActivityState extends State {
-        boolean activityIn;
-        boolean activityOut;
-    }
-    static class RSSIState extends ActivityState {
-        int signalIconId;
-        String signalContentDescription;
-        int dataTypeIconId;
-        String dataContentDescription;
-    }
-    static class WifiState extends ActivityState {
-        String signalContentDescription;
-        boolean connected;
-    }
-    static class UserState extends State {
-        Drawable avatar;
-    }
-    static class BrightnessState extends State {
-        boolean autoBrightness;
-    }
-    static class InversionState extends State {
-        boolean toggled;
-    }
-    static class ColorSpaceState extends State {
-        boolean toggled;
-        int type;
-    }
-    public static class BluetoothState extends State {
-        boolean connected = false;
-        String stateContentDescription;
-    }
-    public static class RotationLockState extends State {
-        boolean visible = false;
-    }
-    public static class ZenModeState extends State {
-        int zenMode = Settings.Global.ZEN_MODE_OFF;
-    }
-
-    /** The callback to update a given tile. */
-    interface RefreshCallback {
-        public void refreshView(QuickSettingsTileView view, State state);
-    }
-
-    public static class BasicRefreshCallback implements RefreshCallback {
-        private final QuickSettingsBasicTile mView;
-        private boolean mShowWhenEnabled;
-
-        public BasicRefreshCallback(QuickSettingsBasicTile v) {
-            mView = v;
-        }
-        public void refreshView(QuickSettingsTileView ignored, State state) {
-            if (mShowWhenEnabled) {
-                mView.setVisibility(state.enabled ? View.VISIBLE : View.GONE);
-            }
-            if (state.iconId != 0) {
-                mView.setImageDrawable(null); // needed to flush any cached IDs
-                mView.setImageResource(state.iconId);
-            }
-            if (state.label != null) {
-                mView.setText(state.label);
-            }
-        }
-        public BasicRefreshCallback setShowWhenEnabled(boolean swe) {
-            mShowWhenEnabled = swe;
-            return this;
-        }
-    }
-
-    /** Broadcast receive to determine if there is an alarm set. */
-    private BroadcastReceiver mAlarmIntentReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (action.equals(Intent.ACTION_ALARM_CHANGED)) {
-                onAlarmChanged(intent);
-                onNextAlarmChanged();
-            }
-        }
-    };
-
-    /** ContentObserver to determine the next alarm */
-    private class NextAlarmObserver extends ContentObserver {
-        public NextAlarmObserver(Handler handler) {
-            super(handler);
-        }
-
-        @Override public void onChange(boolean selfChange) {
-            onNextAlarmChanged();
-        }
-
-        public void startObserving() {
-            final ContentResolver cr = mContext.getContentResolver();
-            cr.registerContentObserver(
-                    Settings.System.getUriFor(Settings.System.NEXT_ALARM_FORMATTED), false, this,
-                    UserHandle.USER_ALL);
-        }
-    }
-
-    /** ContentObserver to watch adb */
-    private class BugreportObserver extends ContentObserver {
-        public BugreportObserver(Handler handler) {
-            super(handler);
-        }
-
-        @Override public void onChange(boolean selfChange) {
-            onBugreportChanged();
-        }
-
-        public void startObserving() {
-            final ContentResolver cr = mContext.getContentResolver();
-            cr.registerContentObserver(
-                    Settings.Global.getUriFor(Settings.Global.BUGREPORT_IN_POWER_MENU), false, this);
-        }
-    }
-
-    /** ContentObserver to watch brightness **/
-    private class BrightnessObserver extends ContentObserver {
-        public BrightnessObserver(Handler handler) {
-            super(handler);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            onBrightnessLevelChanged();
-        }
-
-        public void startObserving() {
-            final ContentResolver cr = mContext.getContentResolver();
-            cr.unregisterContentObserver(this);
-            cr.registerContentObserver(
-                    Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE),
-                    false, this, mUserTracker.getCurrentUserId());
-            cr.registerContentObserver(
-                    Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS),
-                    false, this, mUserTracker.getCurrentUserId());
-        }
-    }
-
-    /** ContentObserver to watch display inversion */
-    private class DisplayInversionObserver extends ContentObserver {
-        public DisplayInversionObserver(Handler handler) {
-            super(handler);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            onInversionChanged();
-        }
-
-        public void startObserving() {
-            final ContentResolver cr = mContext.getContentResolver();
-            cr.unregisterContentObserver(this);
-            cr.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED),
-                    false, this, mUserTracker.getCurrentUserId());
-            cr.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_QUICK_SETTING_ENABLED),
-                    false, this, mUserTracker.getCurrentUserId());
-        }
-    }
-
-    /** ContentObserver to watch display color space adjustment */
-    private class DisplayColorSpaceObserver extends ContentObserver {
-        public DisplayColorSpaceObserver(Handler handler) {
-            super(handler);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            onColorSpaceChanged();
-        }
-
-        public void startObserving() {
-            final ContentResolver cr = mContext.getContentResolver();
-            cr.unregisterContentObserver(this);
-            cr.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
-                    false, this, mUserTracker.getCurrentUserId());
-            cr.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_QUICK_SETTING_ENABLED),
-                    false, this, mUserTracker.getCurrentUserId());
-            cr.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER),
-                    false, this, mUserTracker.getCurrentUserId());
-        }
-    }
-
-    /** ContentObserver to watch display color space adjustment */
-    private class ZenModeObserver extends ContentObserver {
-        public ZenModeObserver(Handler handler) {
-            super(handler);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            onZenModeChanged();
-        }
-
-        public void startObserving() {
-            final ContentResolver cr = mContext.getContentResolver();
-            cr.unregisterContentObserver(this);
-            cr.registerContentObserver(
-                    Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false, this);
-        }
-    }
-
-    /** Callback for changes to remote display routes. */
-    private class RemoteDisplayRouteCallback extends MediaRouter.SimpleCallback {
-        @Override
-        public void onRouteAdded(MediaRouter router, RouteInfo route) {
-            updateRemoteDisplays();
-        }
-        @Override
-        public void onRouteChanged(MediaRouter router, RouteInfo route) {
-            updateRemoteDisplays();
-        }
-        @Override
-        public void onRouteRemoved(MediaRouter router, RouteInfo route) {
-            updateRemoteDisplays();
-        }
-        @Override
-        public void onRouteSelected(MediaRouter router, int type, RouteInfo route) {
-            updateRemoteDisplays();
-        }
-        @Override
-        public void onRouteUnselected(MediaRouter router, int type, RouteInfo route) {
-            updateRemoteDisplays();
-        }
-    }
-
-    private final Context mContext;
-    private final Handler mHandler;
-    private final CurrentUserTracker mUserTracker;
-    private final NextAlarmObserver mNextAlarmObserver;
-    private final BugreportObserver mBugreportObserver;
-    private final BrightnessObserver mBrightnessObserver;
-    private final DisplayInversionObserver mInversionObserver;
-    private final DisplayColorSpaceObserver mColorSpaceObserver;
-    private final ZenModeObserver mZenModeObserver;
-
-    private final MediaRouter mMediaRouter;
-    private final RemoteDisplayRouteCallback mRemoteDisplayRouteCallback;
-
-    private final boolean mHasMobileData;
-
-    private QuickSettingsTileView mUserTile;
-    private RefreshCallback mUserCallback;
-    private UserState mUserState = new UserState();
-
-    private QuickSettingsTileView mTimeTile;
-    private RefreshCallback mTimeCallback;
-    private State mTimeState = new State();
-
-    private QuickSettingsTileView mAlarmTile;
-    private RefreshCallback mAlarmCallback;
-    private State mAlarmState = new State();
-
-    private QuickSettingsTileView mAirplaneModeTile;
-    private RefreshCallback mAirplaneModeCallback;
-    private State mAirplaneModeState = new State();
-
-    private QuickSettingsTileView mZenModeTile;
-    private RefreshCallback mZenModeCallback;
-    private ZenModeState mZenModeState = new ZenModeState();
-
-    private QuickSettingsTileView mWifiTile;
-    private RefreshCallback mWifiCallback;
-    private WifiState mWifiState = new WifiState();
-
-    private QuickSettingsTileView mRemoteDisplayTile;
-    private RefreshCallback mRemoteDisplayCallback;
-    private State mRemoteDisplayState = new State();
-
-    private QuickSettingsTileView mRSSITile;
-    private RefreshCallback mRSSICallback;
-    private RSSIState mRSSIState = new RSSIState();
-
-    private QuickSettingsTileView mBluetoothTile;
-    private RefreshCallback mBluetoothCallback;
-    private BluetoothState mBluetoothState = new BluetoothState();
-
-    private QuickSettingsTileView mBatteryTile;
-    private RefreshCallback mBatteryCallback;
-    private BatteryState mBatteryState = new BatteryState();
-
-    private QuickSettingsTileView mLocationTile;
-    private RefreshCallback mLocationCallback;
-    private State mLocationState = new State();
-
-    private QuickSettingsTileView mImeTile;
-    private RefreshCallback mImeCallback = null;
-    private State mImeState = new State();
-
-    private QuickSettingsTileView mRotationLockTile;
-    private RefreshCallback mRotationLockCallback;
-    private RotationLockState mRotationLockState = new RotationLockState();
-
-    private QuickSettingsTileView mBrightnessTile;
-    private RefreshCallback mBrightnessCallback;
-    private BrightnessState mBrightnessState = new BrightnessState();
-
-    private QuickSettingsTileView mInversionTile;
-    private RefreshCallback mInversionCallback;
-    private InversionState mInversionState = new InversionState();
-
-    private QuickSettingsTileView mColorSpaceTile;
-    private RefreshCallback mColorSpaceCallback;
-    private ColorSpaceState mColorSpaceState = new ColorSpaceState();
-
-    private QuickSettingsTileView mBugreportTile;
-    private RefreshCallback mBugreportCallback;
-    private State mBugreportState = new State();
-
-    private QuickSettingsTileView mSettingsTile;
-    private RefreshCallback mSettingsCallback;
-    private State mSettingsState = new State();
-
-    private QuickSettingsTileView mSslCaCertWarningTile;
-    private RefreshCallback mSslCaCertWarningCallback;
-    private State mSslCaCertWarningState = new State();
-
-    private RotationLockController mRotationLockController;
-    private int mRotationLockedLabel;
-
-    public QuickSettingsModel(Context context) {
-        mContext = context;
-        mHandler = new Handler();
-        mUserTracker = new CurrentUserTracker(mContext) {
-            @Override
-            public void onUserSwitched(int newUserId) {
-                mBrightnessObserver.startObserving();
-                mInversionObserver.startObserving();
-                mColorSpaceObserver.startObserving();
-                refreshRotationLockTile();
-                onBrightnessLevelChanged();
-                onInversionChanged();
-                onColorSpaceChanged();
-                onNextAlarmChanged();
-                onBugreportChanged();
-                rebindMediaRouterAsCurrentUser();
-            }
-        };
-        mUserTracker.startTracking();
-
-        mNextAlarmObserver = new NextAlarmObserver(mHandler);
-        mNextAlarmObserver.startObserving();
-        mBugreportObserver = new BugreportObserver(mHandler);
-        mBugreportObserver.startObserving();
-        mBrightnessObserver = new BrightnessObserver(mHandler);
-        mBrightnessObserver.startObserving();
-        mInversionObserver = new DisplayInversionObserver(mHandler);
-        mInversionObserver.startObserving();
-        mColorSpaceObserver = new DisplayColorSpaceObserver(mHandler);
-        mColorSpaceObserver.startObserving();
-        mZenModeObserver = new ZenModeObserver(mHandler);
-        mZenModeObserver.startObserving();
-
-        mMediaRouter = (MediaRouter)context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
-        rebindMediaRouterAsCurrentUser();
-
-        mRemoteDisplayRouteCallback = new RemoteDisplayRouteCallback();
-
-        ConnectivityManager cm = (ConnectivityManager)
-                context.getSystemService(Context.CONNECTIVITY_SERVICE);
-        mHasMobileData = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
-
-        IntentFilter alarmIntentFilter = new IntentFilter();
-        alarmIntentFilter.addAction(Intent.ACTION_ALARM_CHANGED);
-        context.registerReceiver(mAlarmIntentReceiver, alarmIntentFilter);
-    }
-
-    void updateResources() {
-        refreshSettingsTile();
-        refreshBatteryTile();
-        refreshBluetoothTile();
-        refreshBrightnessTile();
-        refreshRotationLockTile();
-        refreshRssiTile();
-        refreshLocationTile();
-    }
-
-    // Settings
-    void addSettingsTile(QuickSettingsTileView view, RefreshCallback cb) {
-        mSettingsTile = view;
-        mSettingsCallback = cb;
-        refreshSettingsTile();
-    }
-    void refreshSettingsTile() {
-        Resources r = mContext.getResources();
-        mSettingsState.label = r.getString(R.string.quick_settings_settings_label);
-        mSettingsCallback.refreshView(mSettingsTile, mSettingsState);
-    }
-
-    // User
-    void addUserTile(QuickSettingsTileView view, RefreshCallback cb) {
-        mUserTile = view;
-        mUserCallback = cb;
-        mUserCallback.refreshView(mUserTile, mUserState);
-    }
-    void setUserTileInfo(String name, Drawable avatar) {
-        mUserState.label = name;
-        mUserState.avatar = avatar;
-        mUserCallback.refreshView(mUserTile, mUserState);
-    }
-
-    // Time
-    void addTimeTile(QuickSettingsTileView view, RefreshCallback cb) {
-        mTimeTile = view;
-        mTimeCallback = cb;
-        mTimeCallback.refreshView(view, mTimeState);
-    }
-
-    // Alarm
-    void addAlarmTile(QuickSettingsTileView view, RefreshCallback cb) {
-        mAlarmTile = view;
-        mAlarmCallback = cb;
-        mAlarmCallback.refreshView(view, mAlarmState);
-    }
-    void onAlarmChanged(Intent intent) {
-        mAlarmState.enabled = intent.getBooleanExtra("alarmSet", false);
-        mAlarmCallback.refreshView(mAlarmTile, mAlarmState);
-    }
-    void onNextAlarmChanged() {
-        final String alarmText = Settings.System.getStringForUser(mContext.getContentResolver(),
-                Settings.System.NEXT_ALARM_FORMATTED,
-                UserHandle.USER_CURRENT);
-        mAlarmState.label = alarmText;
-
-        // When switching users, this is the only clue we're going to get about whether the
-        // alarm is actually set, since we won't get the ACTION_ALARM_CHANGED broadcast
-        mAlarmState.enabled = ! TextUtils.isEmpty(alarmText);
-
-        mAlarmCallback.refreshView(mAlarmTile, mAlarmState);
-    }
-
-    // Airplane Mode
-    void addAirplaneModeTile(QuickSettingsTileView view, RefreshCallback cb) {
-        mAirplaneModeTile = view;
-        mAirplaneModeTile.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if (mAirplaneModeState.enabled) {
-                    setAirplaneModeState(false);
-                } else {
-                    setAirplaneModeState(true);
-                }
-            }
-        });
-        mAirplaneModeCallback = cb;
-        int airplaneMode = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_ON, 0);
-        onAirplaneModeChanged(airplaneMode != 0);
-    }
-    private void setAirplaneModeState(boolean enabled) {
-        // TODO: Sets the view to be "awaiting" if not already awaiting
-
-        // Change the system setting
-        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON,
-                                enabled ? 1 : 0);
-
-        // Post the intent
-        Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        intent.putExtra("state", enabled);
-        mContext.sendBroadcast(intent);
-    }
-    // NetworkSignalChanged callback
-    @Override
-    public void onAirplaneModeChanged(boolean enabled) {
-        // TODO: If view is in awaiting state, disable
-        Resources r = mContext.getResources();
-        mAirplaneModeState.enabled = enabled;
-        mAirplaneModeState.iconId = (enabled ?
-                R.drawable.ic_qs_airplane_on :
-                R.drawable.ic_qs_airplane_off);
-        mAirplaneModeState.label = r.getString(R.string.quick_settings_airplane_mode_label);
-        mAirplaneModeCallback.refreshView(mAirplaneModeTile, mAirplaneModeState);
-    }
-
-    // Zen Mode
-    void addZenModeTile(QuickSettingsTileView view, RefreshCallback cb) {
-        mZenModeTile = view;
-        mZenModeCallback = cb;
-        onZenModeChanged();
-    }
-    private void onZenModeChanged() {
-        final int mode = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
-        mZenModeState.enabled = mode != Settings.Global.ZEN_MODE_OFF;
-        mZenModeState.zenMode = mode;
-        mZenModeState.label = mContext.getString(R.string.zen_mode_title);
-        mZenModeState.iconId = R.drawable.stat_sys_zen_limited;
-        mZenModeCallback.refreshView(mZenModeTile, mZenModeState);
-    }
-
-    // Wifi
-    void addWifiTile(QuickSettingsTileView view, RefreshCallback cb) {
-        mWifiTile = view;
-        mWifiCallback = cb;
-        mWifiCallback.refreshView(mWifiTile, mWifiState);
-    }
-    // Remove the double quotes that the SSID may contain
-    public static String removeDoubleQuotes(String string) {
-        if (string == null) return null;
-        final int length = string.length();
-        if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
-            return string.substring(1, length - 1);
-        }
-        return string;
-    }
-    // Remove the period from the network name
-    public static String removeTrailingPeriod(String string) {
-        if (string == null) return null;
-        final int length = string.length();
-        if (string.endsWith(".")) {
-            return string.substring(0, length - 1);
-        }
-        return string;
-    }
-    // NetworkSignalChanged callback
-    @Override
-    public void onWifiSignalChanged(boolean enabled, int wifiSignalIconId,
-            boolean activityIn, boolean activityOut,
-            String wifiSignalContentDescription, String enabledDesc) {
-        // TODO: If view is in awaiting state, disable
-        Resources r = mContext.getResources();
-
-        boolean wifiConnected = enabled && (wifiSignalIconId > 0) && (enabledDesc != null);
-        boolean wifiNotConnected = (wifiSignalIconId > 0) && (enabledDesc == null);
-        mWifiState.enabled = enabled;
-        mWifiState.connected = wifiConnected;
-        mWifiState.activityIn = enabled && activityIn;
-        mWifiState.activityOut = enabled && activityOut;
-        if (wifiConnected) {
-            mWifiState.iconId = wifiSignalIconId;
-            mWifiState.label = removeDoubleQuotes(enabledDesc);
-            mWifiState.signalContentDescription = wifiSignalContentDescription;
-        } else if (wifiNotConnected) {
-            mWifiState.iconId = R.drawable.ic_qs_wifi_0;
-            mWifiState.label = r.getString(R.string.quick_settings_wifi_label);
-            mWifiState.signalContentDescription = r.getString(R.string.accessibility_no_wifi);
-        } else {
-            mWifiState.iconId = R.drawable.ic_qs_wifi_no_network;
-            mWifiState.label = r.getString(R.string.quick_settings_wifi_off_label);
-            mWifiState.signalContentDescription = r.getString(R.string.accessibility_wifi_off);
-        }
-        mWifiCallback.refreshView(mWifiTile, mWifiState);
-    }
-
-    boolean deviceHasMobileData() {
-        return mHasMobileData;
-    }
-
-    // RSSI
-    void addRSSITile(QuickSettingsTileView view, RefreshCallback cb) {
-        mRSSITile = view;
-        mRSSICallback = cb;
-        mRSSICallback.refreshView(mRSSITile, mRSSIState);
-    }
-    // NetworkSignalChanged callback
-    @Override
-    public void onMobileDataSignalChanged(
-            boolean enabled, int mobileSignalIconId, String signalContentDescription,
-            int dataTypeIconId, boolean activityIn, boolean activityOut,
-            String dataContentDescription,String enabledDesc) {
-        if (deviceHasMobileData()) {
-            // TODO: If view is in awaiting state, disable
-            Resources r = mContext.getResources();
-            mRSSIState.signalIconId = enabled && (mobileSignalIconId > 0)
-                    ? mobileSignalIconId
-                    : R.drawable.ic_qs_signal_no_signal;
-            mRSSIState.signalContentDescription = enabled && (mobileSignalIconId > 0)
-                    ? signalContentDescription
-                    : r.getString(R.string.accessibility_no_signal);
-            mRSSIState.dataTypeIconId = enabled && (dataTypeIconId > 0) && !mWifiState.enabled
-                    ? dataTypeIconId
-                    : 0;
-            mRSSIState.activityIn = enabled && activityIn;
-            mRSSIState.activityOut = enabled && activityOut;
-            mRSSIState.dataContentDescription = enabled && (dataTypeIconId > 0) && !mWifiState.enabled
-                    ? dataContentDescription
-                    : r.getString(R.string.accessibility_no_data);
-            mRSSIState.label = enabled
-                    ? removeTrailingPeriod(enabledDesc)
-                    : r.getString(R.string.quick_settings_rssi_emergency_only);
-            mRSSICallback.refreshView(mRSSITile, mRSSIState);
-        }
-    }
-
-    void refreshRssiTile() {
-        if (mRSSITile != null) {
-            // We reinflate the original view due to potential styling changes that may have
-            // taken place due to a configuration change.
-            mRSSITile.reinflateContent(LayoutInflater.from(mContext));
-        }
-    }
-
-    // Bluetooth
-    void addBluetoothTile(QuickSettingsTileView view, RefreshCallback cb) {
-        mBluetoothTile = view;
-        mBluetoothCallback = cb;
-
-        final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        mBluetoothState.enabled = adapter.isEnabled();
-        mBluetoothState.connected =
-                (adapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTED);
-        onBluetoothStateChange(mBluetoothState);
-    }
-    boolean deviceSupportsBluetooth() {
-        return (BluetoothAdapter.getDefaultAdapter() != null);
-    }
-    // BluetoothController callback
-    @Override
-    public void onBluetoothStateChange(boolean on) {
-        mBluetoothState.enabled = on;
-        onBluetoothStateChange(mBluetoothState);
-    }
-    public void onBluetoothStateChange(BluetoothState bluetoothStateIn) {
-        // TODO: If view is in awaiting state, disable
-        Resources r = mContext.getResources();
-        mBluetoothState.enabled = bluetoothStateIn.enabled;
-        mBluetoothState.connected = bluetoothStateIn.connected;
-        if (mBluetoothState.enabled) {
-            if (mBluetoothState.connected) {
-                mBluetoothState.iconId = R.drawable.ic_qs_bluetooth_on;
-                mBluetoothState.stateContentDescription = r.getString(R.string.accessibility_desc_connected);
-            } else {
-                mBluetoothState.iconId = R.drawable.ic_qs_bluetooth_not_connected;
-                mBluetoothState.stateContentDescription = r.getString(R.string.accessibility_desc_on);
-            }
-            mBluetoothState.label = r.getString(R.string.quick_settings_bluetooth_label);
-        } else {
-            mBluetoothState.iconId = R.drawable.ic_qs_bluetooth_off;
-            mBluetoothState.label = r.getString(R.string.quick_settings_bluetooth_off_label);
-            mBluetoothState.stateContentDescription = r.getString(R.string.accessibility_desc_off);
-        }
-        mBluetoothCallback.refreshView(mBluetoothTile, mBluetoothState);
-    }
-    void refreshBluetoothTile() {
-        if (mBluetoothTile != null) {
-            onBluetoothStateChange(mBluetoothState.enabled);
-        }
-    }
-
-    // Battery
-    void addBatteryTile(QuickSettingsTileView view, RefreshCallback cb) {
-        mBatteryTile = view;
-        mBatteryCallback = cb;
-        mBatteryCallback.refreshView(mBatteryTile, mBatteryState);
-    }
-    // BatteryController callback
-    @Override
-    public void onBatteryLevelChanged(int level, boolean pluggedIn) {
-        mBatteryState.batteryLevel = level;
-        mBatteryState.pluggedIn = pluggedIn;
-        mBatteryCallback.refreshView(mBatteryTile, mBatteryState);
-    }
-    void refreshBatteryTile() {
-        mBatteryCallback.refreshView(mBatteryTile, mBatteryState);
-    }
-
-    // Location
-    void addLocationTile(QuickSettingsTileView view, RefreshCallback cb) {
-        mLocationTile = view;
-        mLocationCallback = cb;
-        mLocationCallback.refreshView(mLocationTile, mLocationState);
-    }
-
-    void refreshLocationTile() {
-        if (mLocationTile != null) {
-            onLocationSettingsChanged(mLocationState.enabled);
-        }
-    }
-
-    @Override
-    public void onLocationSettingsChanged(boolean locationEnabled) {
-        int textResId = locationEnabled ? R.string.quick_settings_location_label
-                : R.string.quick_settings_location_off_label;
-        String label = mContext.getText(textResId).toString();
-        int locationIconId = locationEnabled
-                ? R.drawable.ic_qs_location_on : R.drawable.ic_qs_location_off;
-        mLocationState.enabled = locationEnabled;
-        mLocationState.label = label;
-        mLocationState.iconId = locationIconId;
-        mLocationCallback.refreshView(mLocationTile, mLocationState);
-    }
-
-    // Bug report
-    void addBugreportTile(QuickSettingsTileView view, RefreshCallback cb) {
-        mBugreportTile = view;
-        mBugreportCallback = cb;
-        onBugreportChanged();
-    }
-    // SettingsObserver callback
-    public void onBugreportChanged() {
-        final ContentResolver cr = mContext.getContentResolver();
-        boolean enabled = false;
-        try {
-            enabled = (Settings.Global.getInt(cr, Settings.Global.BUGREPORT_IN_POWER_MENU) != 0);
-        } catch (SettingNotFoundException e) {
-        }
-
-        mBugreportState.enabled = enabled && mUserTracker.isCurrentUserOwner();
-        mBugreportCallback.refreshView(mBugreportTile, mBugreportState);
-    }
-
-    // Remote Display
-    void addRemoteDisplayTile(QuickSettingsTileView view, RefreshCallback cb) {
-        mRemoteDisplayTile = view;
-        mRemoteDisplayCallback = cb;
-        mRemoteDisplayTile.setOnPrepareListener(new QuickSettingsTileView.OnPrepareListener() {
-            @Override
-            public void onPrepare() {
-                mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
-                        mRemoteDisplayRouteCallback,
-                        MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
-                updateRemoteDisplays();
-            }
-            @Override
-            public void onUnprepare() {
-                mMediaRouter.removeCallback(mRemoteDisplayRouteCallback);
-            }
-        });
-
-        updateRemoteDisplays();
-    }
-
-    private void rebindMediaRouterAsCurrentUser() {
-        mMediaRouter.rebindAsUser(mUserTracker.getCurrentUserId());
-    }
-
-    private void updateRemoteDisplays() {
-        MediaRouter.RouteInfo connectedRoute = mMediaRouter.getSelectedRoute(
-                MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
-        boolean enabled = connectedRoute != null
-                && connectedRoute.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
-        boolean connecting;
-        if (enabled) {
-            connecting = connectedRoute.isConnecting();
-        } else {
-            connectedRoute = null;
-            connecting = false;
-            enabled = mMediaRouter.isRouteAvailable(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
-                    MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE);
-        }
-
-        mRemoteDisplayState.enabled = enabled;
-        if (connectedRoute != null) {
-            mRemoteDisplayState.label = connectedRoute.getName().toString();
-            mRemoteDisplayState.iconId = connecting ?
-                    R.drawable.ic_qs_cast_connecting : R.drawable.ic_qs_cast_connected;
-        } else {
-            mRemoteDisplayState.label = mContext.getString(
-                    R.string.quick_settings_remote_display_no_connection_label);
-            mRemoteDisplayState.iconId = R.drawable.ic_qs_cast_available;
-        }
-        mRemoteDisplayCallback.refreshView(mRemoteDisplayTile, mRemoteDisplayState);
-    }
-
-    // IME
-    void addImeTile(QuickSettingsTileView view, RefreshCallback cb) {
-        mImeTile = view;
-        mImeCallback = cb;
-        mImeCallback.refreshView(mImeTile, mImeState);
-    }
-    /* This implementation is taken from
-       InputMethodManagerService.needsToShowImeSwitchOngoingNotification(). */
-    private boolean needsToShowImeSwitchOngoingNotification(InputMethodManager imm) {
-        List<InputMethodInfo> imis = imm.getEnabledInputMethodList();
-        final int N = imis.size();
-        if (N > 2) return true;
-        if (N < 1) return false;
-        int nonAuxCount = 0;
-        int auxCount = 0;
-        InputMethodSubtype nonAuxSubtype = null;
-        InputMethodSubtype auxSubtype = null;
-        for(int i = 0; i < N; ++i) {
-            final InputMethodInfo imi = imis.get(i);
-            final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(imi,
-                    true);
-            final int subtypeCount = subtypes.size();
-            if (subtypeCount == 0) {
-                ++nonAuxCount;
-            } else {
-                for (int j = 0; j < subtypeCount; ++j) {
-                    final InputMethodSubtype subtype = subtypes.get(j);
-                    if (!subtype.isAuxiliary()) {
-                        ++nonAuxCount;
-                        nonAuxSubtype = subtype;
-                    } else {
-                        ++auxCount;
-                        auxSubtype = subtype;
-                    }
-                }
-            }
-        }
-        if (nonAuxCount > 1 || auxCount > 1) {
-            return true;
-        } else if (nonAuxCount == 1 && auxCount == 1) {
-            if (nonAuxSubtype != null && auxSubtype != null
-                    && (nonAuxSubtype.getLocale().equals(auxSubtype.getLocale())
-                            || auxSubtype.overridesImplicitlyEnabledSubtype()
-                            || nonAuxSubtype.overridesImplicitlyEnabledSubtype())
-                    && nonAuxSubtype.containsExtraValueKey(TAG_TRY_SUPPRESSING_IME_SWITCHER)) {
-                return false;
-            }
-            return true;
-        }
-        return false;
-    }
-    void onImeWindowStatusChanged(boolean visible) {
-        InputMethodManager imm =
-                (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
-        List<InputMethodInfo> imis = imm.getInputMethodList();
-
-        mImeState.enabled = (visible && needsToShowImeSwitchOngoingNotification(imm));
-        mImeState.label = getCurrentInputMethodName(mContext, mContext.getContentResolver(),
-                imm, imis, mContext.getPackageManager());
-        if (mImeCallback != null) {
-            mImeCallback.refreshView(mImeTile, mImeState);
-        }
-    }
-    private static String getCurrentInputMethodName(Context context, ContentResolver resolver,
-            InputMethodManager imm, List<InputMethodInfo> imis, PackageManager pm) {
-        if (resolver == null || imis == null) return null;
-        final String currentInputMethodId = Settings.Secure.getString(resolver,
-                Settings.Secure.DEFAULT_INPUT_METHOD);
-        if (TextUtils.isEmpty(currentInputMethodId)) return null;
-        for (InputMethodInfo imi : imis) {
-            if (currentInputMethodId.equals(imi.getId())) {
-                final InputMethodSubtype subtype = imm.getCurrentInputMethodSubtype();
-                final CharSequence summary = subtype != null
-                        ? subtype.getDisplayName(context, imi.getPackageName(),
-                                imi.getServiceInfo().applicationInfo)
-                        : context.getString(R.string.quick_settings_ime_label);
-                return summary.toString();
-            }
-        }
-        return null;
-    }
-
-    // Rotation lock
-    void addRotationLockTile(QuickSettingsTileView view,
-            RotationLockController rotationLockController,
-            RefreshCallback cb) {
-        mRotationLockTile = view;
-        mRotationLockCallback = cb;
-        mRotationLockController = rotationLockController;
-        final int lockOrientation = mRotationLockController.getRotationLockOrientation();
-        mRotationLockedLabel = lockOrientation == Configuration.ORIENTATION_PORTRAIT
-                    ? R.string.quick_settings_rotation_locked_portrait_label
-                    : lockOrientation == Configuration.ORIENTATION_LANDSCAPE
-                    ? R.string.quick_settings_rotation_locked_landscape_label
-                    : R.string.quick_settings_rotation_locked_label;
-        onRotationLockChanged();
-    }
-    void onRotationLockChanged() {
-        onRotationLockStateChanged(mRotationLockController.isRotationLocked(),
-                mRotationLockController.isRotationLockAffordanceVisible());
-    }
-    @Override
-    public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) {
-        mRotationLockState.visible = affordanceVisible;
-        mRotationLockState.enabled = rotationLocked;
-        mRotationLockState.iconId = rotationLocked
-                ? R.drawable.ic_qs_rotation_locked
-                : R.drawable.ic_qs_auto_rotate;
-        mRotationLockState.label = rotationLocked
-                ? mContext.getString(mRotationLockedLabel)
-                : mContext.getString(R.string.quick_settings_rotation_unlocked_label);
-        mRotationLockCallback.refreshView(mRotationLockTile, mRotationLockState);
-    }
-    void refreshRotationLockTile() {
-        if (mRotationLockTile != null) {
-            onRotationLockChanged();
-        }
-    }
-
-    // Brightness
-    void addBrightnessTile(QuickSettingsTileView view, RefreshCallback cb) {
-        mBrightnessTile = view;
-        mBrightnessCallback = cb;
-        onBrightnessLevelChanged();
-    }
-    @Override
-    public void onBrightnessLevelChanged() {
-        Resources r = mContext.getResources();
-        int mode = Settings.System.getIntForUser(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
-                mUserTracker.getCurrentUserId());
-        mBrightnessState.autoBrightness =
-                (mode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-        mBrightnessState.iconId = mBrightnessState.autoBrightness
-                ? R.drawable.ic_qs_brightness_auto_on
-                : R.drawable.ic_qs_brightness_auto_off;
-        mBrightnessState.label = r.getString(R.string.quick_settings_brightness_label);
-        mBrightnessCallback.refreshView(mBrightnessTile, mBrightnessState);
-    }
-    void refreshBrightnessTile() {
-        onBrightnessLevelChanged();
-    }
-
-    // Color inversion
-    void addInversionTile(QuickSettingsTileView view, RefreshCallback cb) {
-        mInversionTile = view;
-        mInversionCallback = cb;
-        onInversionChanged();
-    }
-    public void onInversionChanged() {
-        final Resources res = mContext.getResources();
-        final ContentResolver cr = mContext.getContentResolver();
-        final int currentUserId = mUserTracker.getCurrentUserId();
-        final boolean quickSettingEnabled = Settings.Secure.getIntForUser(
-                cr, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_QUICK_SETTING_ENABLED, 0,
-                currentUserId) == 1;
-        final boolean enabled = Settings.Secure.getIntForUser(
-                cr, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, currentUserId) == 1;
-        mInversionState.enabled = quickSettingEnabled;
-        mInversionState.toggled = enabled;
-        // TODO: Add real icon assets.
-        mInversionState.iconId = enabled ? R.drawable.ic_qs_inversion_on
-                : R.drawable.ic_qs_inversion_off;
-        mInversionState.label = res.getString(R.string.quick_settings_inversion_label);
-        mInversionCallback.refreshView(mInversionTile, mInversionState);
-    }
-
-    // Color space adjustment
-    void addColorSpaceTile(QuickSettingsTileView view, RefreshCallback cb) {
-        mColorSpaceTile = view;
-        mColorSpaceCallback = cb;
-        onColorSpaceChanged();
-    }
-    public void onColorSpaceChanged() {
-        final Resources res = mContext.getResources();
-        final ContentResolver cr = mContext.getContentResolver();
-        final int currentUserId = mUserTracker.getCurrentUserId();
-        final boolean quickSettingEnabled = Settings.Secure.getIntForUser(
-                cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_QUICK_SETTING_ENABLED, 0,
-                currentUserId) == 1;
-        final boolean enabled = Settings.Secure.getIntForUser(cr,
-                Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, currentUserId) == 1;
-        final int type = Settings.Secure.getIntForUser(
-                cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, 0, currentUserId);
-        mColorSpaceState.enabled = quickSettingEnabled;
-        mColorSpaceState.toggled = enabled;
-        mColorSpaceState.type = type;
-        // TODO: Add real icon assets.
-        mColorSpaceState.iconId = enabled ? R.drawable.ic_qs_color_space_on
-                : R.drawable.ic_qs_color_space_off;
-        mColorSpaceState.label = res.getString(R.string.quick_settings_color_space_label);
-        mColorSpaceCallback.refreshView(mColorSpaceTile, mColorSpaceState);
-    }
-
-    // SSL CA Cert warning.
-    public void addSslCaCertWarningTile(QuickSettingsTileView view, RefreshCallback cb) {
-        mSslCaCertWarningTile = view;
-        mSslCaCertWarningCallback = cb;
-        // Set a sane default while we wait for the AsyncTask to finish (no cert).
-        setSslCaCertWarningTileInfo(false, true);
-    }
-    public void setSslCaCertWarningTileInfo(boolean hasCert, boolean isManaged) {
-        Resources r = mContext.getResources();
-        mSslCaCertWarningState.enabled = hasCert;
-        if (isManaged) {
-            mSslCaCertWarningState.iconId = R.drawable.ic_qs_certificate_info;
-        } else {
-            mSslCaCertWarningState.iconId = android.R.drawable.stat_notify_error;
-        }
-        mSslCaCertWarningState.label = r.getString(R.string.ssl_ca_cert_warning);
-        mSslCaCertWarningCallback.refreshView(mSslCaCertWarningTile, mSslCaCertWarningState);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsScrollView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsScrollView.java
deleted file mode 100644
index 175805a..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsScrollView.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.ScrollView;
-
-public class QuickSettingsScrollView extends ScrollView {
-
-    public QuickSettingsScrollView(Context context) {
-        super(context);
-    }
-
-    public QuickSettingsScrollView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public QuickSettingsScrollView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    // Y U NO PROTECTED
-    private int getScrollRange() {
-        int scrollRange = 0;
-        if (getChildCount() > 0) {
-            View child = getChildAt(0);
-            scrollRange = Math.max(0,
-                    child.getHeight() - (getHeight() - getPaddingBottom() - getPaddingTop()));
-        }
-        return scrollRange;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        final int range = getScrollRange();
-        if (range == 0) {
-            return false;
-        }
-
-        return super.onTouchEvent(ev);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsTileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsTileView.java
deleted file mode 100644
index ad18294..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsTileView.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewParent;
-import android.widget.FrameLayout;
-
-/**
- *
- */
-class QuickSettingsTileView extends FrameLayout {
-    private static final String TAG = "QuickSettingsTileView";
-
-    private int mContentLayoutId;
-    private int mColSpan;
-    private boolean mPrepared;
-    private OnPrepareListener mOnPrepareListener;
-
-    public QuickSettingsTileView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        mContentLayoutId = -1;
-        mColSpan = 1;
-    }
-
-    void setColumnSpan(int span) {
-        mColSpan = span;
-    }
-
-    int getColumnSpan() {
-        return mColSpan;
-    }
-
-    void setContent(int layoutId, LayoutInflater inflater) {
-        mContentLayoutId = layoutId;
-        inflater.inflate(layoutId, this);
-    }
-
-    void reinflateContent(LayoutInflater inflater) {
-        if (mContentLayoutId != -1) {
-            removeAllViews();
-            setContent(mContentLayoutId, inflater);
-        } else {
-            Log.e(TAG, "Not reinflating content: No layoutId set");
-        }
-    }
-
-    @Override
-    public void setVisibility(int vis) {
-        if (QuickSettings.DEBUG_GONE_TILES) {
-            if (vis == View.GONE) {
-                vis = View.VISIBLE;
-                setAlpha(0.25f);
-                setEnabled(false);
-            } else {
-                setAlpha(1f);
-                setEnabled(true);
-            }
-        }
-        super.setVisibility(vis);
-    }
-
-    public void setOnPrepareListener(OnPrepareListener listener) {
-        if (mOnPrepareListener != listener) {
-            mOnPrepareListener = listener;
-            mPrepared = false;
-            post(new Runnable() {
-                @Override
-                public void run() {
-                    updatePreparedState();
-                }
-            });
-        }
-    }
-
-    @Override
-    protected void onVisibilityChanged(View changedView, int visibility) {
-        super.onVisibilityChanged(changedView, visibility);
-        updatePreparedState();
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        updatePreparedState();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        updatePreparedState();
-    }
-
-    private void updatePreparedState() {
-        if (mOnPrepareListener != null) {
-            if (isParentVisible()) {
-                if (!mPrepared) {
-                    mPrepared = true;
-                    mOnPrepareListener.onPrepare();
-                }
-            } else if (mPrepared) {
-                mPrepared = false;
-                mOnPrepareListener.onUnprepare();
-            }
-        }
-    }
-
-    private boolean isParentVisible() {
-        if (!isAttachedToWindow()) {
-            return false;
-        }
-        for (ViewParent current = getParent(); current instanceof View;
-                current = current.getParent()) {
-            View view = (View)current;
-            if (view.getVisibility() != VISIBLE) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Called when the view's parent becomes visible or invisible to provide
-     * an opportunity for the client to provide new content.
-     */
-    public interface OnPrepareListener {
-        void onPrepare();
-        void onUnprepare();
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 5527473..2305445 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -28,6 +28,7 @@
 import android.widget.RelativeLayout;
 
 import com.android.systemui.R;
+import com.android.systemui.qs.QSPanel;
 import com.android.systemui.settings.BrightnessController;
 import com.android.systemui.settings.ToggleSlider;
 import com.android.systemui.statusbar.policy.UserInfoController;
@@ -60,6 +61,7 @@
 
     private ActivityStarter mActivityStarter;
     private BrightnessController mBrightnessController;
+    private QSPanel mQSPanel;
 
     private final Rect mClipBounds = new Rect();
     private final Outline mOutline = new Outline();
@@ -115,6 +117,9 @@
             updateVisibilities();
             updateSystemIconsLayoutParams();
             updateBrightnessControllerState();
+            if (mQSPanel != null) {
+                mQSPanel.setExpanded(expanded);
+            }
         }
     }
 
@@ -259,4 +264,8 @@
     private void startSettingsActivity() {
         mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS));
     }
+
+    public void setQSPanel(QSPanel qsp) {
+        mQSPanel = qsp;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java
deleted file mode 100644
index ff921cd..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.Typeface;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.OvalShape;
-import android.text.Spannable;
-import android.text.SpannableStringBuilder;
-import android.text.TextPaint;
-import android.text.method.LinkMovementMethod;
-import android.text.style.URLSpan;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
-import android.widget.Switch;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.ZenModeView.Adapter.ExitCondition;
-
-public class ZenModeView extends RelativeLayout {
-    private static final String TAG = ZenModeView.class.getSimpleName();
-    private static final boolean DEBUG = false;
-
-    public static final int BACKGROUND = 0xff282828;
-
-    private static final Typeface CONDENSED =
-            Typeface.create("sans-serif-condensed", Typeface.NORMAL);
-    private static final int GRAY = 0xff999999; //TextAppearance.StatusBar.Expanded.Network
-    private static final int DARK_GRAY = 0xff333333;
-
-    private static final long DURATION = new ValueAnimator().getDuration();
-    private static final long PAGER_DURATION = DURATION / 2;
-    private static final long CLOSE_DELAY = 600;
-    private static final long AUTO_ACTIVATE_DELAY = 100;
-
-    private final Context mContext;
-    private final TextView mModeText;
-    private final Switch mModeSwitch;
-    private final View mDivider;
-    private final UntilPager mUntilPager;
-    private final ProgressDots mProgressDots;
-    private final View mDivider2;
-    private final TextView mSettingsButton;
-
-    private Adapter mAdapter;
-    private boolean mInit;
-    private boolean mAutoActivate;
-
-    public ZenModeView(Context context) {
-        this(context, null);
-    }
-
-    public ZenModeView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        if (DEBUG) log("new %s()", getClass().getSimpleName());
-        mContext = context;
-
-        final int iconSize = mContext.getResources()
-                .getDimensionPixelSize(com.android.internal.R.dimen.notification_large_icon_width);
-        final int topRowSize = iconSize * 2 / 3;
-        final int p = topRowSize / 3;
-
-        LayoutParams lp = null;
-
-        mModeText = new TextView(mContext);
-        mModeText.setText(R.string.zen_mode_title);
-        mModeText.setId(android.R.id.title);
-        mModeText.setTextColor(GRAY);
-        mModeText.setTypeface(CONDENSED);
-        mModeText.setAllCaps(true);
-        mModeText.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
-        mModeText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mModeText.getTextSize() * 1.5f);
-        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, topRowSize);
-        lp.leftMargin = p;
-        addView(mModeText, lp);
-
-        mModeSwitch = new Switch(mContext);
-        mModeSwitch.setSwitchPadding(0);
-        mModeSwitch.setSwitchTypeface(CONDENSED);
-        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, topRowSize);
-        lp.topMargin = p;
-        lp.rightMargin = p;
-        lp.addRule(ALIGN_PARENT_RIGHT);
-        lp.addRule(ALIGN_BASELINE, mModeText.getId());
-        addView(mModeSwitch, lp);
-        mModeSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-                mAdapter.setMode(isChecked);
-                if (!mInit) return;
-                postDelayed(new Runnable(){
-                    @Override
-                    public void run() {
-                        mAdapter.close();
-                    }
-                }, CLOSE_DELAY);
-            }
-        });
-
-        mDivider = new View(mContext);
-        mDivider.setId(android.R.id.empty);
-        mDivider.setBackgroundColor(GRAY);
-        lp = new LayoutParams(LayoutParams.MATCH_PARENT, 2);
-        lp.addRule(BELOW, mModeText.getId());
-        lp.bottomMargin = p;
-        addView(mDivider, lp);
-
-        mUntilPager = new UntilPager(mContext, iconSize * 3 / 4);
-        mUntilPager.setId(android.R.id.tabhost);
-        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
-        lp.leftMargin = lp.rightMargin = iconSize / 2;
-        lp.addRule(CENTER_HORIZONTAL);
-        lp.addRule(BELOW, mDivider.getId());
-        addView(mUntilPager, lp);
-
-        mProgressDots = new ProgressDots(mContext, iconSize / 5);
-        mProgressDots.setId(android.R.id.progress);
-        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
-        lp.addRule(CENTER_HORIZONTAL);
-        lp.addRule(BELOW, mUntilPager.getId());
-        addView(mProgressDots, lp);
-
-        mDivider2 = new View(mContext);
-        mDivider2.setId(android.R.id.widget_frame);
-        mDivider2.setBackgroundColor(GRAY);
-        lp = new LayoutParams(LayoutParams.MATCH_PARENT, 2);
-        lp.addRule(BELOW, mProgressDots.getId());
-        addView(mDivider2, lp);
-
-        mSettingsButton = new TextView(mContext);
-        mSettingsButton.setTypeface(CONDENSED);
-        mSettingsButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, mSettingsButton.getTextSize() * 1.3f);
-        mSettingsButton.setPadding(p, p, p, p);
-        mSettingsButton.setText("More settings...");
-        lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
-        lp.addRule(BELOW, mDivider2.getId());
-        addView(mSettingsButton, lp);
-        mSettingsButton.setOnTouchListener(new OnTouchListener() {
-            @Override
-            public boolean onTouch(View v, MotionEvent event) {
-                if (event.getAction() == MotionEvent.ACTION_DOWN) {
-                    mSettingsButton.setBackgroundColor(DARK_GRAY);
-                } else if (event.getAction() == MotionEvent.ACTION_UP) {
-                    mSettingsButton.setBackground(null);
-                    if (mAdapter != null) {
-                        mAdapter.configure();
-                    }
-                }
-                return true;
-            }
-        });
-    }
-
-    public void setAdapter(Adapter adapter) {
-        mAdapter = adapter;
-        mAdapter.setCallbacks(new Adapter.Callbacks() {
-            @Override
-            public void onChanged() {
-                post(new Runnable() {
-                    @Override
-                    public void run() {
-                        updateState(true);
-                    }
-                });
-            }
-        });
-        updateState(false);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        if (mAutoActivate) {
-            mAutoActivate = false;
-            postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    if (!mModeSwitch.isChecked()) {
-                        mInit = false;
-                        mModeSwitch.setChecked(true);
-                    }
-                }
-            }, AUTO_ACTIVATE_DELAY);
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        if (mAdapter != null) {
-            mAdapter.dispose();
-        }
-    }
-
-    public void setAutoActivate(boolean value) {
-        mAutoActivate = value;
-    }
-
-    private void updateState(boolean animate) {
-        mUntilPager.updateState();
-        mModeSwitch.setChecked(mAdapter.getMode());
-        mInit = true;
-    }
-
-    private static void log(String msg, Object... args) {
-        Log.d(TAG, args == null || args.length == 0 ? msg : String.format(msg, args));
-    }
-
-    private final class UntilView extends FrameLayout {
-        private static final boolean SUPPORT_LINKS = false;
-
-        private final TextView mText;
-        public UntilView(Context context) {
-            super(context);
-            mText = new TextView(mContext);
-            mText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mText.getTextSize() * 1.3f);
-            mText.setTypeface(CONDENSED);
-            mText.setTextColor(GRAY);
-            mText.setGravity(Gravity.CENTER);
-            addView(mText);
-        }
-
-        public void setExitCondition(final ExitCondition ec) {
-            SpannableStringBuilder ss = new SpannableStringBuilder(ec.summary);
-            if (SUPPORT_LINKS && ec.action != null) {
-                ss.setSpan(new CustomLinkSpan() {
-                    @Override
-                    public void onClick() {
-                        // TODO wire up links
-                        Toast.makeText(mContext, ec.action, Toast.LENGTH_SHORT).show();
-                    }
-                }, 0, ss.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
-                mText.setMovementMethod(LinkMovementMethod.getInstance());
-            } else {
-                mText.setMovementMethod(null);
-            }
-            mText.setText(ss);
-        }
-    }
-
-    private final class ProgressDots extends LinearLayout {
-        private final int mDotSize;
-        public ProgressDots(Context context, int dotSize) {
-            super(context);
-            setOrientation(HORIZONTAL);
-            mDotSize = dotSize;
-        }
-
-        private void updateState(int current, int count) {
-            while (getChildCount() < count) {
-                View dot = new View(mContext);
-                OvalShape s = new OvalShape();
-                ShapeDrawable sd = new ShapeDrawable(s);
-
-                dot.setBackground(sd);
-                LayoutParams lp = new LayoutParams(mDotSize, mDotSize);
-                lp.leftMargin = lp.rightMargin = mDotSize / 2;
-                lp.topMargin = lp.bottomMargin = mDotSize * 2 / 3;
-                addView(dot, lp);
-            }
-            while (getChildCount() > count) {
-                removeViewAt(getChildCount() - 1);
-            }
-            final int N = getChildCount();
-            for (int i = 0; i < N; i++) {
-                final int color = current == i ? GRAY : DARK_GRAY;
-                ((ShapeDrawable)getChildAt(i).getBackground()).setColorFilter(color, Mode.ADD);
-            }
-        }
-    }
-
-    private final class UntilPager extends RelativeLayout {
-        private final UntilView[] mViews;
-        private int mCurrent;
-        private float mDownX;
-
-        public UntilPager(Context context, int iconSize) {
-            super(context);
-            mViews = new UntilView[3];
-            for (int i = 0; i < mViews.length; i++) {
-                UntilView v = new UntilView(mContext);
-                LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, iconSize);
-                addView(v, lp);
-                mViews[i] = v;
-            }
-            updateState();
-            addOnLayoutChangeListener(new OnLayoutChangeListener() {
-                @Override
-                public void onLayoutChange(View v, int left, int top, int right,
-                        int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
-                    if (left != oldLeft || right != oldRight) {
-                        updateState();
-                    }
-                }
-            });
-            setBackgroundColor(DARK_GRAY);
-        }
-
-        private void updateState() {
-            if (mAdapter == null) {
-                return;
-            }
-            UntilView current = mViews[mCurrent];
-            current.setExitCondition(mAdapter.getExitCondition(0));
-            UntilView next = mViews[mCurrent + 1 % 3];
-            next.setExitCondition(mAdapter.getExitCondition(1));
-            UntilView prev = mViews[mCurrent + 2 % 3];
-            prev.setExitCondition(mAdapter.getExitCondition(-1));
-            position(0, false);
-            mProgressDots.updateState(mAdapter.getExitConditionIndex(),
-                    mAdapter.getExitConditionCount());
-        }
-
-        private void position(float dx, boolean animate) {
-            int w = getWidth();
-            UntilView current = mViews[mCurrent];
-            UntilView next = mViews[mCurrent + 1 % 3];
-            UntilView prev = mViews[mCurrent + 2 % 3];
-            if (animate) {
-                current.animate().setDuration(PAGER_DURATION).translationX(dx).start();
-                next.animate().setDuration(PAGER_DURATION).translationX(w + dx).start();
-                prev.animate().setDuration(PAGER_DURATION).translationX(-w + dx).start();
-            } else {
-                current.setTranslationX(dx);
-                next.setTranslationX(w + dx);
-                prev.setTranslationX(-w + dx);
-            }
-        }
-
-        @Override
-        public boolean onTouchEvent(MotionEvent event) {
-            if (DEBUG) log("onTouchEvent " + MotionEvent.actionToString(event.getAction()));
-            if (event.getAction() == MotionEvent.ACTION_DOWN) {
-                mDownX = event.getX();
-            } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
-                float dx = event.getX() - mDownX;
-                position(dx, false);
-            } else if (event.getAction() == MotionEvent.ACTION_UP
-                    || event.getAction() == MotionEvent.ACTION_CANCEL) {
-                float dx = event.getX() - mDownX;
-                int d = Math.abs(dx) < getWidth() / 3 ? 0 : Math.signum(dx) > 0 ? -1 : 1;
-                if (d != 0 && mAdapter.getExitConditionCount() > 1) {
-                    mAdapter.select(mAdapter.getExitCondition(d));
-                } else {
-                    position(0, true);
-                }
-            }
-            return true;
-        }
-    }
-
-    private abstract static class CustomLinkSpan extends URLSpan {
-        abstract public void onClick();
-
-        public CustomLinkSpan() {
-            super("#");
-        }
-
-        @Override
-        public void updateDrawState(TextPaint ds) {
-            super.updateDrawState(ds);
-            ds.setUnderlineText(false);
-            ds.bgColor = BACKGROUND;
-        }
-
-        @Override
-        public void onClick(View widget) {
-            onClick();
-        }
-    }
-
-    public interface Adapter {
-        void configure();
-        void close();
-        boolean getMode();
-        void setMode(boolean mode);
-        void select(ExitCondition ec);
-        void init();
-        void dispose();
-        void setCallbacks(Callbacks callbacks);
-        ExitCondition getExitCondition(int d);
-        int getExitConditionCount();
-        int getExitConditionIndex();
-
-        public static class ExitCondition {
-            public String summary;
-            public String line1;
-            public String line2;
-            public String action;
-            public Object tag;
-        }
-
-        public interface Callbacks {
-            void onChanged();
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java
deleted file mode 100644
index 8748888..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.app.INotificationManager;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.provider.Settings;
-import android.service.notification.Condition;
-import android.service.notification.IConditionListener;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-public abstract class ZenModeViewAdapter implements ZenModeView.Adapter {
-    private static final String TAG = "ZenModeViewAdapter";
-
-    private final Context mContext;
-    private final ContentResolver mResolver;
-    private final Handler mHandler = new Handler();
-    private final SettingsObserver mObserver;
-    private final List<ExitCondition> mExits = new ArrayList<ExitCondition>(Arrays.asList(
-            newExit("Until you turn this off", "Until", "You turn this off", null)));
-    private final INotificationManager mNoMan;
-    private final ArrayMap<Uri, Condition> mConditions = new ArrayMap<Uri, Condition>();
-
-    private Callbacks mCallbacks;
-    private int mExitIndex;
-    private boolean mMode;
-
-    public ZenModeViewAdapter(Context context) {
-        mContext = context;
-        mResolver = mContext.getContentResolver();
-        mObserver = new SettingsObserver(mHandler);
-        mNoMan = INotificationManager.Stub.asInterface(
-                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
-        try {
-            mNoMan.requestZenModeConditions(mListener, Condition.FLAG_RELEVANT_NOW);
-        } catch (RemoteException e) {
-            // noop
-        }
-        mObserver.init();
-        init();
-    }
-
-    @Override
-    public boolean getMode() {
-        return mMode;
-    }
-
-    @Override
-    public void setMode(boolean mode) {
-        if (mode == mMode) return;
-        mMode = mode;
-        final int v = mMode ? Settings.Global.ZEN_MODE_ON : Settings.Global.ZEN_MODE_OFF;
-        AsyncTask.execute(new Runnable() {
-            @Override
-            public void run() {
-                Settings.Global.putInt(mContext.getContentResolver(),
-                        Settings.Global.ZEN_MODE, v);
-            }
-        });
-        dispatchChanged();
-    }
-
-    @Override
-    public void init() {
-        if (mExitIndex != 0) {
-            mExitIndex = 0;
-            dispatchChanged();
-        }
-        setZenModeCondition();
-    }
-
-    @Override
-    public void dispose() {
-        try {
-            mNoMan.requestZenModeConditions(mListener, 0 /*none*/);
-        } catch (RemoteException e) {
-            // noop
-        }
-    }
-
-    private void dispatchChanged() {
-        mHandler.removeCallbacks(mChanged);
-        mHandler.post(mChanged);
-    }
-
-    @Override
-    public void setCallbacks(final Callbacks callbacks) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                mCallbacks = callbacks;
-            }
-        });
-    }
-
-    @Override
-    public ExitCondition getExitCondition(int d) {
-        final int n = mExits.size();
-        final int i = (n + (mExitIndex + (int)Math.signum(d))) % n;
-        return mExits.get(i);
-    }
-
-    @Override
-    public int getExitConditionCount() {
-        return mExits.size();
-    }
-
-    @Override
-    public int getExitConditionIndex() {
-        return mExitIndex;
-    }
-
-    @Override
-    public void select(ExitCondition ec) {
-        final int i = mExits.indexOf(ec);
-        if (i == -1 || i == mExitIndex) {
-            return;
-        }
-        mExitIndex = i;
-        dispatchChanged();
-        setZenModeCondition();
-    }
-
-    private void setZenModeCondition() {
-        if (mExitIndex < 0 || mExitIndex >= mExits.size()) {
-            Log.w(TAG, "setZenModeCondition to bad index " + mExitIndex + " of " + mExits.size());
-            return;
-        }
-        final Uri conditionUri = (Uri) mExits.get(mExitIndex).tag;
-        try {
-            mNoMan.setZenModeCondition(conditionUri);
-        } catch (RemoteException e) {
-            // noop
-        }
-    }
-
-    private static ExitCondition newExit(String summary, String line1, String line2, Object tag) {
-        final ExitCondition rt = new ExitCondition();
-        rt.summary = summary;
-        rt.line1 = line1;
-        rt.line2 = line2;
-        rt.tag = tag;
-        return rt;
-    }
-
-    private final Runnable mChanged = new Runnable() {
-        public void run() {
-            if (mCallbacks == null) {
-                return;
-            }
-            try {
-                mCallbacks.onChanged();
-            } catch (Throwable t) {
-                Log.w(TAG, "Error dispatching onChanged to " + mCallbacks, t);
-            }
-        }
-    };
-
-    private final class SettingsObserver extends ContentObserver {
-        public SettingsObserver(Handler handler) {
-            super(handler);
-        }
-
-        public void init() {
-            loadSettings();
-            mResolver.registerContentObserver(
-                    Settings.Global.getUriFor(Settings.Global.ZEN_MODE),
-                    false, this);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            loadSettings();
-            mChanged.run();  // already on handler
-        }
-
-        private void loadSettings() {
-            mMode = getModeFromSetting();
-        }
-
-        private boolean getModeFromSetting() {
-            final int v = Settings.Global.getInt(mResolver,
-                    Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
-            return v != Settings.Global.ZEN_MODE_OFF;
-        }
-    }
-
-    private final IConditionListener mListener = new IConditionListener.Stub() {
-        @Override
-        public void onConditionsReceived(Condition[] conditions) {
-            if (conditions == null || conditions.length == 0) return;
-            for (Condition c : conditions) {
-                mConditions.put(c.id, c);
-            }
-            for (int i = mExits.size() - 1; i > 0; i--) {
-                mExits.remove(i);
-            }
-            for (Condition c : mConditions.values()) {
-                mExits.add(newExit(c.summary, c.line1, c.line2, c.id));
-            }
-            dispatchChanged();
-        }
-    };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
index 0e53f0d..f4145cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,87 +16,14 @@
 
 package com.android.systemui.statusbar.policy;
 
-import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothAdapter.BluetoothStateChangeCallback;
-import android.bluetooth.BluetoothDevice;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
+public interface BluetoothController {
+    void addStateChangedCallback(BluetoothStateChangeCallback callback);
+    void removeStateChangedCallback(BluetoothStateChangeCallback callback);
 
-public class BluetoothController extends BroadcastReceiver {
-    private static final String TAG = "StatusBar.BluetoothController";
-
-    private boolean mEnabled = false;
-
-    private Set<BluetoothDevice> mBondedDevices = new HashSet<BluetoothDevice>();
-
-    private ArrayList<BluetoothStateChangeCallback> mChangeCallbacks =
-            new ArrayList<BluetoothStateChangeCallback>();
-
-    public BluetoothController(Context context) {
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
-        filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
-        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
-        context.registerReceiver(this, filter);
-
-        final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        if (adapter != null) {
-            handleAdapterStateChange(adapter.getState());
-        }
-        fireCallbacks();
-        updateBondedBluetoothDevices();
-    }
-
-    public void addStateChangedCallback(BluetoothStateChangeCallback cb) {
-        mChangeCallbacks.add(cb);
-    }
-
-    public Set<BluetoothDevice> getBondedBluetoothDevices() {
-        return mBondedDevices;
-    }
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        final String action = intent.getAction();
-
-        if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
-            handleAdapterStateChange(
-                    intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR));
-        }
-        fireCallbacks();
-        updateBondedBluetoothDevices();
-    }
-
-    private void updateBondedBluetoothDevices() {
-        mBondedDevices.clear();
-
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        if (adapter != null) {
-            Set<BluetoothDevice> devices = adapter.getBondedDevices();
-            if (devices != null) {
-                for (BluetoothDevice device : devices) {
-                    if (device.getBondState() != BluetoothDevice.BOND_NONE) {
-                        mBondedDevices.add(device);
-                    }
-                }
-            }
-        }
-    }
-
-    private void handleAdapterStateChange(int adapterState) {
-        mEnabled = (adapterState == BluetoothAdapter.STATE_ON);
-    }
-
-    private void fireCallbacks() {
-        for (BluetoothStateChangeCallback cb : mChangeCallbacks) {
-            cb.onBluetoothStateChange(mEnabled);
-        }
-    }
+    boolean isBluetoothSupported();
+    boolean isBluetoothEnabled();
+    boolean isBluetoothConnected();
+    void setBluetoothEnabled(boolean enabled);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
new file mode 100644
index 0000000..1c7119f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothAdapter.BluetoothStateChangeCallback;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+public class BluetoothControllerImpl extends BroadcastReceiver implements BluetoothController {
+    private static final String TAG = "StatusBar.BluetoothController";
+
+    private final BluetoothAdapter mAdapter;
+
+    private boolean mEnabled = false;
+
+    private Set<BluetoothDevice> mBondedDevices = new HashSet<BluetoothDevice>();
+
+    private ArrayList<BluetoothStateChangeCallback> mChangeCallbacks =
+            new ArrayList<BluetoothStateChangeCallback>();
+
+    public BluetoothControllerImpl(Context context) {
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+        filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
+        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+        context.registerReceiver(this, filter);
+
+        final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        if (adapter != null) {
+            handleAdapterStateChange(adapter.getState());
+        }
+        fireCallbacks();
+        updateBondedBluetoothDevices();
+    }
+
+    public void addStateChangedCallback(BluetoothStateChangeCallback cb) {
+        mChangeCallbacks.add(cb);
+    }
+
+    @Override
+    public void removeStateChangedCallback(BluetoothStateChangeCallback cb) {
+        mChangeCallbacks.remove(cb);
+    }
+
+    @Override
+    public boolean isBluetoothEnabled() {
+        return mAdapter != null && mAdapter.isEnabled();
+    }
+
+    @Override
+    public boolean isBluetoothConnected() {
+        return mAdapter != null
+                && mAdapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTED;
+    }
+
+    @Override
+    public void setBluetoothEnabled(boolean enabled) {
+        if (mAdapter != null) {
+            if (enabled) {
+                mAdapter.enable();
+            } else {
+                mAdapter.disable();
+            }
+        }
+    }
+
+    @Override
+    public boolean isBluetoothSupported() {
+        return mAdapter != null;
+    }
+
+    public Set<BluetoothDevice> getBondedBluetoothDevices() {
+        return mBondedDevices;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+
+        if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+            handleAdapterStateChange(
+                    intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR));
+        }
+        fireCallbacks();
+        updateBondedBluetoothDevices();
+    }
+
+    private void updateBondedBluetoothDevices() {
+        mBondedDevices.clear();
+
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        if (adapter != null) {
+            Set<BluetoothDevice> devices = adapter.getBondedDevices();
+            if (devices != null) {
+                for (BluetoothDevice device : devices) {
+                    if (device.getBondState() != BluetoothDevice.BOND_NONE) {
+                        mBondedDevices.add(device);
+                    }
+                }
+            }
+        }
+    }
+
+    private void handleAdapterStateChange(int adapterState) {
+        mEnabled = (adapterState == BluetoothAdapter.STATE_ON);
+    }
+
+    private void fireCallbacks() {
+        for (BluetoothStateChangeCallback cb : mChangeCallbacks) {
+            cb.onBluetoothStateChange(mEnabled);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
new file mode 100644
index 0000000..54041e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+public interface CastController {
+    void addCallback(Callback callback);
+    void removeCallback(Callback callback);
+    void setDiscovering(boolean request);
+    void setCurrentUserId(int currentUserId);
+
+    public interface Callback {
+        void onStateChanged(boolean enabled, boolean connecting, String connectedRouteName);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
new file mode 100644
index 0000000..33a85b1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.content.Context;
+import android.media.MediaRouter;
+import android.media.MediaRouter.RouteInfo;
+
+import java.util.ArrayList;
+
+/** Platform implementation of the cast controller. **/
+public class CastControllerImpl implements CastController {
+
+    private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
+    private final MediaRouter mMediaRouter;
+
+    public CastControllerImpl(Context context) {
+        mMediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
+    }
+
+    @Override
+    public void addCallback(Callback callback) {
+        mCallbacks.add(callback);
+    }
+
+    @Override
+    public void removeCallback(Callback callback) {
+        mCallbacks.remove(callback);
+    }
+
+    @Override
+    public void setDiscovering(boolean request) {
+        if (request) {
+            mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
+                    mMediaCallback,
+                    MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
+        } else {
+            mMediaRouter.removeCallback(mMediaCallback);
+        }
+    }
+
+    @Override
+    public void setCurrentUserId(int currentUserId) {
+        mMediaRouter.rebindAsUser(currentUserId);
+    }
+
+    private void updateRemoteDisplays() {
+        final MediaRouter.RouteInfo connectedRoute = mMediaRouter.getSelectedRoute(
+                MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
+        boolean enabled = connectedRoute != null
+                && connectedRoute.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
+        boolean connecting;
+        if (enabled) {
+            connecting = connectedRoute.isConnecting();
+        } else {
+            connecting = false;
+            enabled = mMediaRouter.isRouteAvailable(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
+                    MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE);
+        }
+
+        String connectedRouteName = null;
+        if (connectedRoute != null) {
+            connectedRouteName = connectedRoute.getName().toString();
+        }
+        fireStateChanged(enabled, connecting, connectedRouteName);
+    }
+
+    private void fireStateChanged(boolean enabled, boolean connecting, String connectedRouteName) {
+        for (Callback callback : mCallbacks) {
+            callback.onStateChanged(enabled, connecting, connectedRouteName);
+        }
+    }
+
+    private final MediaRouter.SimpleCallback mMediaCallback = new MediaRouter.SimpleCallback() {
+        @Override
+        public void onRouteAdded(MediaRouter router, RouteInfo route) {
+            updateRemoteDisplays();
+        }
+        @Override
+        public void onRouteChanged(MediaRouter router, RouteInfo route) {
+            updateRemoteDisplays();
+        }
+        @Override
+        public void onRouteRemoved(MediaRouter router, RouteInfo route) {
+            updateRemoteDisplays();
+        }
+        @Override
+        public void onRouteSelected(MediaRouter router, int type, RouteInfo route) {
+            updateRemoteDisplays();
+        }
+        @Override
+        public void onRouteUnselected(MediaRouter router, int type, RouteInfo route) {
+            updateRemoteDisplays();
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Disposable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Disposable.java
new file mode 100644
index 0000000..158e9c1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Disposable.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+/** Common interface for items requiring manual cleanup. **/
+public interface Disposable {
+    void dispose();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
index f5ee95b..29a8981 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,47 +16,11 @@
 
 package com.android.systemui.statusbar.policy;
 
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.app.StatusBarManager;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.location.LocationManager;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-
-import com.android.systemui.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A controller to manage changes of location related states and update the views accordingly.
- */
-public class LocationController extends BroadcastReceiver {
-    // The name of the placeholder corresponding to the location request status icon.
-    // This string corresponds to config_statusBarIcons in core/res/res/values/config.xml.
-    public static final String LOCATION_STATUS_ICON_PLACEHOLDER = "location";
-    public static final int LOCATION_STATUS_ICON_ID
-        = R.drawable.stat_sys_device_access_location_found;
-
-    private static final int[] mHighPowerRequestAppOpArray
-        = new int[] {AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION};
-
-    private Context mContext;
-
-    private AppOpsManager mAppOpsManager;
-    private StatusBarManager mStatusBarManager;
-
-    private boolean mAreActiveLocationRequests;
-
-    private ArrayList<LocationSettingsChangeCallback> mSettingsChangeCallbacks =
-            new ArrayList<LocationSettingsChangeCallback>();
+public interface LocationController {
+    boolean isLocationEnabled();
+    boolean setLocationEnabled(boolean enabled);
+    void addSettingsChangedCallback(LocationSettingsChangeCallback cb);
+    void removeSettingsChangedCallback(LocationSettingsChangeCallback cb);
 
     /**
      * A callback for change in location settings (the user has enabled/disabled location).
@@ -68,156 +32,6 @@
          * @param locationEnabled A value of true indicates that at least one type of location
          *                        is enabled in settings.
          */
-        public void onLocationSettingsChanged(boolean locationEnabled);
-    }
-
-    public LocationController(Context context) {
-        mContext = context;
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
-        context.registerReceiver(this, filter);
-
-        mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
-        mStatusBarManager
-                = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE);
-
-        // Register to listen for changes in location settings.
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(LocationManager.MODE_CHANGED_ACTION);
-        context.registerReceiverAsUser(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                String action = intent.getAction();
-                if (LocationManager.MODE_CHANGED_ACTION.equals(action)) {
-                    locationSettingsChanged();
-                }
-            }
-        }, UserHandle.ALL, intentFilter, null, new Handler());
-
-        // Examine the current location state and initialize the status view.
-        updateActiveLocationRequests();
-        refreshViews();
-    }
-
-    /**
-     * Add a callback to listen for changes in location settings.
-     */
-    public void addSettingsChangedCallback(LocationSettingsChangeCallback cb) {
-        mSettingsChangeCallbacks.add(cb);
-    }
-
-    /**
-     * Enable or disable location in settings.
-     *
-     * <p>This will attempt to enable/disable every type of location setting
-     * (e.g. high and balanced power).
-     *
-     * <p>If enabling, a user consent dialog will pop up prompting the user to accept.
-     * If the user doesn't accept, network location won't be enabled.
-     *
-     * @return true if attempt to change setting was successful.
-     */
-    public boolean setLocationEnabled(boolean enabled) {
-        int currentUserId = ActivityManager.getCurrentUser();
-        if (isUserLocationRestricted(currentUserId)) {
-            return false;
-        }
-        final ContentResolver cr = mContext.getContentResolver();
-        // When enabling location, a user consent dialog will pop up, and the
-        // setting won't be fully enabled until the user accepts the agreement.
-        int mode = enabled
-                ? Settings.Secure.LOCATION_MODE_HIGH_ACCURACY : Settings.Secure.LOCATION_MODE_OFF;
-        // QuickSettings always runs as the owner, so specifically set the settings
-        // for the current foreground user.
-        return Settings.Secure
-                .putIntForUser(cr, Settings.Secure.LOCATION_MODE, mode, currentUserId);
-    }
-
-    /**
-     * Returns true if location isn't disabled in settings.
-     */
-    public boolean isLocationEnabled() {
-        ContentResolver resolver = mContext.getContentResolver();
-        // QuickSettings always runs as the owner, so specifically retrieve the settings
-        // for the current foreground user.
-        int mode = Settings.Secure.getIntForUser(resolver, Settings.Secure.LOCATION_MODE,
-                Settings.Secure.LOCATION_MODE_OFF, ActivityManager.getCurrentUser());
-        return mode != Settings.Secure.LOCATION_MODE_OFF;
-    }
-
-    /**
-     * Returns true if the current user is restricted from using location.
-     */
-    private boolean isUserLocationRestricted(int userId) {
-        final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        return um.hasUserRestriction(
-                UserManager.DISALLOW_SHARE_LOCATION,
-                new UserHandle(userId));
-    }
-
-    /**
-     * Returns true if there currently exist active high power location requests.
-     */
-    private boolean areActiveHighPowerLocationRequests() {
-        List<AppOpsManager.PackageOps> packages
-            = mAppOpsManager.getPackagesForOps(mHighPowerRequestAppOpArray);
-        // AppOpsManager can return null when there is no requested data.
-        if (packages != null) {
-            final int numPackages = packages.size();
-            for (int packageInd = 0; packageInd < numPackages; packageInd++) {
-                AppOpsManager.PackageOps packageOp = packages.get(packageInd);
-                List<AppOpsManager.OpEntry> opEntries = packageOp.getOps();
-                if (opEntries != null) {
-                    final int numOps = opEntries.size();
-                    for (int opInd = 0; opInd < numOps; opInd++) {
-                        AppOpsManager.OpEntry opEntry = opEntries.get(opInd);
-                        // AppOpsManager should only return OP_MONITOR_HIGH_POWER_LOCATION because
-                        // of the mHighPowerRequestAppOpArray filter, but checking defensively.
-                        if (opEntry.getOp() == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) {
-                            if (opEntry.isRunning()) {
-                                return true;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        return false;
-    }
-
-    // Updates the status view based on the current state of location requests.
-    private void refreshViews() {
-        if (mAreActiveLocationRequests) {
-            mStatusBarManager.setIcon(LOCATION_STATUS_ICON_PLACEHOLDER, LOCATION_STATUS_ICON_ID, 0,
-                    mContext.getString(R.string.accessibility_location_active));
-        } else {
-            mStatusBarManager.removeIcon(LOCATION_STATUS_ICON_PLACEHOLDER);
-        }
-    }
-
-    // Reads the active location requests and updates the status view if necessary.
-    private void updateActiveLocationRequests() {
-        boolean hadActiveLocationRequests = mAreActiveLocationRequests;
-        mAreActiveLocationRequests = areActiveHighPowerLocationRequests();
-        if (mAreActiveLocationRequests != hadActiveLocationRequests) {
-            refreshViews();
-        }
-    }
-
-    private void locationSettingsChanged() {
-        boolean isEnabled = isLocationEnabled();
-        for (LocationSettingsChangeCallback cb : mSettingsChangeCallbacks) {
-            cb.onLocationSettingsChanged(isEnabled);
-        }
-    }
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        final String action = intent.getAction();
-        if (LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)) {
-            updateActiveLocationRequests();
-        }
+        void onLocationSettingsChanged(boolean locationEnabled);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
new file mode 100644
index 0000000..9e5ad18
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.location.LocationManager;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+
+import com.android.systemui.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A controller to manage changes of location related states and update the views accordingly.
+ */
+public class LocationControllerImpl extends BroadcastReceiver implements LocationController {
+    // The name of the placeholder corresponding to the location request status icon.
+    // This string corresponds to config_statusBarIcons in core/res/res/values/config.xml.
+    public static final String LOCATION_STATUS_ICON_PLACEHOLDER = "location";
+    public static final int LOCATION_STATUS_ICON_ID
+        = R.drawable.stat_sys_device_access_location_found;
+
+    private static final int[] mHighPowerRequestAppOpArray
+        = new int[] {AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION};
+
+    private Context mContext;
+
+    private AppOpsManager mAppOpsManager;
+    private StatusBarManager mStatusBarManager;
+
+    private boolean mAreActiveLocationRequests;
+
+    private ArrayList<LocationSettingsChangeCallback> mSettingsChangeCallbacks =
+            new ArrayList<LocationSettingsChangeCallback>();
+
+    public LocationControllerImpl(Context context) {
+        mContext = context;
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
+        context.registerReceiver(this, filter);
+
+        mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+        mStatusBarManager
+                = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE);
+
+        // Register to listen for changes in location settings.
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(LocationManager.MODE_CHANGED_ACTION);
+        context.registerReceiverAsUser(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String action = intent.getAction();
+                if (LocationManager.MODE_CHANGED_ACTION.equals(action)) {
+                    locationSettingsChanged();
+                }
+            }
+        }, UserHandle.ALL, intentFilter, null, new Handler());
+
+        // Examine the current location state and initialize the status view.
+        updateActiveLocationRequests();
+        refreshViews();
+    }
+
+    /**
+     * Add a callback to listen for changes in location settings.
+     */
+    public void addSettingsChangedCallback(LocationSettingsChangeCallback cb) {
+        mSettingsChangeCallbacks.add(cb);
+    }
+
+    public void removeSettingsChangedCallback(LocationSettingsChangeCallback cb) {
+        mSettingsChangeCallbacks.remove(cb);
+    }
+
+    /**
+     * Enable or disable location in settings.
+     *
+     * <p>This will attempt to enable/disable every type of location setting
+     * (e.g. high and balanced power).
+     *
+     * <p>If enabling, a user consent dialog will pop up prompting the user to accept.
+     * If the user doesn't accept, network location won't be enabled.
+     *
+     * @return true if attempt to change setting was successful.
+     */
+    public boolean setLocationEnabled(boolean enabled) {
+        int currentUserId = ActivityManager.getCurrentUser();
+        if (isUserLocationRestricted(currentUserId)) {
+            return false;
+        }
+        final ContentResolver cr = mContext.getContentResolver();
+        // When enabling location, a user consent dialog will pop up, and the
+        // setting won't be fully enabled until the user accepts the agreement.
+        int mode = enabled
+                ? Settings.Secure.LOCATION_MODE_HIGH_ACCURACY : Settings.Secure.LOCATION_MODE_OFF;
+        // QuickSettings always runs as the owner, so specifically set the settings
+        // for the current foreground user.
+        return Settings.Secure
+                .putIntForUser(cr, Settings.Secure.LOCATION_MODE, mode, currentUserId);
+    }
+
+    /**
+     * Returns true if location isn't disabled in settings.
+     */
+    public boolean isLocationEnabled() {
+        ContentResolver resolver = mContext.getContentResolver();
+        // QuickSettings always runs as the owner, so specifically retrieve the settings
+        // for the current foreground user.
+        int mode = Settings.Secure.getIntForUser(resolver, Settings.Secure.LOCATION_MODE,
+                Settings.Secure.LOCATION_MODE_OFF, ActivityManager.getCurrentUser());
+        return mode != Settings.Secure.LOCATION_MODE_OFF;
+    }
+
+    /**
+     * Returns true if the current user is restricted from using location.
+     */
+    private boolean isUserLocationRestricted(int userId) {
+        final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        return um.hasUserRestriction(
+                UserManager.DISALLOW_SHARE_LOCATION,
+                new UserHandle(userId));
+    }
+
+    /**
+     * Returns true if there currently exist active high power location requests.
+     */
+    private boolean areActiveHighPowerLocationRequests() {
+        List<AppOpsManager.PackageOps> packages
+            = mAppOpsManager.getPackagesForOps(mHighPowerRequestAppOpArray);
+        // AppOpsManager can return null when there is no requested data.
+        if (packages != null) {
+            final int numPackages = packages.size();
+            for (int packageInd = 0; packageInd < numPackages; packageInd++) {
+                AppOpsManager.PackageOps packageOp = packages.get(packageInd);
+                List<AppOpsManager.OpEntry> opEntries = packageOp.getOps();
+                if (opEntries != null) {
+                    final int numOps = opEntries.size();
+                    for (int opInd = 0; opInd < numOps; opInd++) {
+                        AppOpsManager.OpEntry opEntry = opEntries.get(opInd);
+                        // AppOpsManager should only return OP_MONITOR_HIGH_POWER_LOCATION because
+                        // of the mHighPowerRequestAppOpArray filter, but checking defensively.
+                        if (opEntry.getOp() == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) {
+                            if (opEntry.isRunning()) {
+                                return true;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    // Updates the status view based on the current state of location requests.
+    private void refreshViews() {
+        if (mAreActiveLocationRequests) {
+            mStatusBarManager.setIcon(LOCATION_STATUS_ICON_PLACEHOLDER, LOCATION_STATUS_ICON_ID, 0,
+                    mContext.getString(R.string.accessibility_location_active));
+        } else {
+            mStatusBarManager.removeIcon(LOCATION_STATUS_ICON_PLACEHOLDER);
+        }
+    }
+
+    // Reads the active location requests and updates the status view if necessary.
+    private void updateActiveLocationRequests() {
+        boolean hadActiveLocationRequests = mAreActiveLocationRequests;
+        mAreActiveLocationRequests = areActiveHighPowerLocationRequests();
+        if (mAreActiveLocationRequests != hadActiveLocationRequests) {
+            refreshViews();
+        }
+    }
+
+    private void locationSettingsChanged() {
+        boolean isEnabled = isLocationEnabled();
+        for (LocationSettingsChangeCallback cb : mSettingsChangeCallbacks) {
+            cb.onLocationSettingsChanged(isEnabled);
+        }
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        if (LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)) {
+            updateActiveLocationRequests();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 92c008e..dc8f315 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,153 +16,12 @@
 
 package com.android.systemui.statusbar.policy;
 
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Resources;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
-import android.net.wimax.WimaxManagerConstants;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.provider.Settings;
-import android.telephony.PhoneStateListener;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-import android.view.View;
-import android.widget.TextView;
+public interface NetworkController {
 
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.cdma.EriInfo;
-import com.android.internal.util.AsyncChannel;
-import com.android.systemui.DemoMode;
-import com.android.systemui.R;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-public class NetworkController extends BroadcastReceiver implements DemoMode {
-    // debug
-    static final String TAG = "StatusBar.NetworkController";
-    static final boolean DEBUG = false;
-    static final boolean CHATTY = false; // additional diagnostics, but not logspew
-
-    private static final int FLIGHT_MODE_ICON = R.drawable.stat_sys_signal_flightmode;
-
-    // telephony
-    boolean mHspaDataDistinguishable;
-    final TelephonyManager mPhone;
-    boolean mDataConnected;
-    IccCardConstants.State mSimState = IccCardConstants.State.READY;
-    int mPhoneState = TelephonyManager.CALL_STATE_IDLE;
-    int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
-    int mDataState = TelephonyManager.DATA_DISCONNECTED;
-    int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
-    ServiceState mServiceState;
-    SignalStrength mSignalStrength;
-    int[] mDataIconList = TelephonyIcons.DATA_G[0];
-    String mNetworkName;
-    String mNetworkNameDefault;
-    String mNetworkNameSeparator;
-    int mPhoneSignalIconId;
-    int mQSPhoneSignalIconId;
-    int mDataDirectionIconId; // data + data direction on phones
-    int mDataSignalIconId;
-    int mDataTypeIconId;
-    int mQSDataTypeIconId;
-    int mAirplaneIconId;
-    boolean mDataActive;
-    int mLastSignalLevel;
-    boolean mShowPhoneRSSIForData = false;
-    boolean mShowAtLeastThreeGees = false;
-    boolean mAlwaysShowCdmaRssi = false;
-
-    String mContentDescriptionPhoneSignal;
-    String mContentDescriptionWifi;
-    String mContentDescriptionWimax;
-    String mContentDescriptionCombinedSignal;
-    String mContentDescriptionDataType;
-
-    // wifi
-    final WifiManager mWifiManager;
-    AsyncChannel mWifiChannel;
-    boolean mWifiEnabled, mWifiConnected;
-    int mWifiRssi, mWifiLevel;
-    String mWifiSsid;
-    int mWifiIconId = 0;
-    int mQSWifiIconId = 0;
-    int mWifiActivity = WifiManager.DATA_ACTIVITY_NONE;
-
-    // bluetooth
-    private boolean mBluetoothTethered = false;
-    private int mBluetoothTetherIconId =
-        com.android.internal.R.drawable.stat_sys_tether_bluetooth;
-
-    //wimax
-    private boolean mWimaxSupported = false;
-    private boolean mIsWimaxEnabled = false;
-    private boolean mWimaxConnected = false;
-    private boolean mWimaxIdle = false;
-    private int mWimaxIconId = 0;
-    private int mWimaxSignal = 0;
-    private int mWimaxState = 0;
-    private int mWimaxExtraState = 0;
-
-    // data connectivity (regardless of state, can we access the internet?)
-    // state of inet connection - 0 not connected, 100 connected
-    private boolean mConnected = false;
-    private int mConnectedNetworkType = ConnectivityManager.TYPE_NONE;
-    private String mConnectedNetworkTypeName;
-    private int mInetCondition = 0;
-    private int mLastInetCondition = 0;
-    private static final int INET_CONDITION_THRESHOLD = 50;
-
-    private boolean mAirplaneMode = false;
-    private boolean mLastAirplaneMode = true;
-
-    private Locale mLocale = null;
-    private Locale mLastLocale = null;
-
-    // our ui
-    Context mContext;
-    ArrayList<TextView> mCombinedLabelViews = new ArrayList<TextView>();
-    ArrayList<TextView> mMobileLabelViews = new ArrayList<TextView>();
-    ArrayList<TextView> mWifiLabelViews = new ArrayList<TextView>();
-    ArrayList<TextView> mEmergencyLabelViews = new ArrayList<TextView>();
-    ArrayList<SignalCluster> mSignalClusters = new ArrayList<SignalCluster>();
-    ArrayList<NetworkSignalChangedCallback> mSignalsChangedCallbacks =
-            new ArrayList<NetworkSignalChangedCallback>();
-    int mLastPhoneSignalIconId = -1;
-    int mLastDataDirectionIconId = -1;
-    int mLastWifiIconId = -1;
-    int mLastWimaxIconId = -1;
-    int mLastCombinedSignalIconId = -1;
-    int mLastDataTypeIconId = -1;
-    String mLastCombinedLabel = "";
-
-    private boolean mHasMobileDataFeature;
-
-    boolean mDataAndWifiStacked = false;
-
-    public interface SignalCluster {
-        void setWifiIndicators(boolean visible, int strengthIcon, boolean problem,
-                String contentDescription);
-        void setMobileDataIndicators(boolean visible, int strengthIcon, boolean problem,
-                int typeIcon, String contentDescription, String typeContentDescription);
-        void setIsAirplaneMode(boolean is, int airplaneIcon);
-    }
+    boolean hasMobileDataFeature();
+    void addNetworkSignalChangedCallback(NetworkSignalChangedCallback cb);
+    void removeNetworkSignalChangedCallback(NetworkSignalChangedCallback cb);
+    void setWifiEnabled(boolean enabled);
 
     public interface NetworkSignalChangedCallback {
         void onWifiSignalChanged(boolean enabled, int wifiSignalIconId,
@@ -174,1304 +33,4 @@
                 String dataTypeContentDescriptionId, String description);
         void onAirplaneModeChanged(boolean enabled);
     }
-
-    /**
-     * Construct this controller object and register for updates.
-     */
-    public NetworkController(Context context) {
-        mContext = context;
-        final Resources res = context.getResources();
-
-        ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
-                Context.CONNECTIVITY_SERVICE);
-        mHasMobileDataFeature = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
-
-        mShowPhoneRSSIForData = res.getBoolean(R.bool.config_showPhoneRSSIForData);
-        mShowAtLeastThreeGees = res.getBoolean(R.bool.config_showMin3G);
-        mAlwaysShowCdmaRssi = res.getBoolean(
-                com.android.internal.R.bool.config_alwaysUseCdmaRssi);
-
-        // set up the default wifi icon, used when no radios have ever appeared
-        updateWifiIcons();
-        updateWimaxIcons();
-
-        // telephony
-        mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
-        mPhone.listen(mPhoneStateListener,
-                          PhoneStateListener.LISTEN_SERVICE_STATE
-                        | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
-                        | PhoneStateListener.LISTEN_CALL_STATE
-                        | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
-                        | PhoneStateListener.LISTEN_DATA_ACTIVITY);
-        mHspaDataDistinguishable = mContext.getResources().getBoolean(
-                R.bool.config_hspa_data_distinguishable);
-        mNetworkNameSeparator = mContext.getString(R.string.status_bar_network_name_separator);
-        mNetworkNameDefault = mContext.getString(
-                com.android.internal.R.string.lockscreen_carrier_default);
-        mNetworkName = mNetworkNameDefault;
-
-        // wifi
-        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-        Handler handler = new WifiHandler();
-        mWifiChannel = new AsyncChannel();
-        Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger();
-        if (wifiMessenger != null) {
-            mWifiChannel.connect(mContext, handler, wifiMessenger);
-        }
-
-        // broadcasts
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
-        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
-        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
-        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
-        filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
-        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
-        filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
-        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
-        filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        mWimaxSupported = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_wimaxEnabled);
-        if(mWimaxSupported) {
-            filter.addAction(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION);
-            filter.addAction(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION);
-            filter.addAction(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION);
-        }
-        context.registerReceiver(this, filter);
-
-        // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it
-        updateAirplaneMode();
-
-        mLastLocale = mContext.getResources().getConfiguration().locale;
-    }
-
-    public boolean hasMobileDataFeature() {
-        return mHasMobileDataFeature;
-    }
-
-    public boolean hasVoiceCallingFeature() {
-        return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
-    }
-
-    public boolean isEmergencyOnly() {
-        return (mServiceState != null && mServiceState.isEmergencyOnly());
-    }
-
-    public void addCombinedLabelView(TextView v) {
-        mCombinedLabelViews.add(v);
-    }
-
-    public void addMobileLabelView(TextView v) {
-        mMobileLabelViews.add(v);
-    }
-
-    public void addWifiLabelView(TextView v) {
-        mWifiLabelViews.add(v);
-    }
-
-    public void addEmergencyLabelView(TextView v) {
-        mEmergencyLabelViews.add(v);
-    }
-
-    public void addSignalCluster(SignalCluster cluster) {
-        mSignalClusters.add(cluster);
-        refreshSignalCluster(cluster);
-    }
-
-    public void addNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) {
-        mSignalsChangedCallbacks.add(cb);
-        notifySignalsChangedCallbacks(cb);
-    }
-
-    public void refreshSignalCluster(SignalCluster cluster) {
-        if (mDemoMode) return;
-        cluster.setWifiIndicators(
-                // only show wifi in the cluster if connected or if wifi-only
-                mWifiEnabled && (mWifiConnected || !mHasMobileDataFeature),
-                mWifiIconId,
-                mInetCondition == 0,
-                mContentDescriptionWifi);
-
-        if (mIsWimaxEnabled && mWimaxConnected) {
-            // wimax is special
-            cluster.setMobileDataIndicators(
-                    true,
-                    mAlwaysShowCdmaRssi ? mPhoneSignalIconId : mWimaxIconId,
-                    mInetCondition == 0,
-                    mDataTypeIconId,
-                    mContentDescriptionWimax,
-                    mContentDescriptionDataType);
-        } else {
-            // normal mobile data
-            cluster.setMobileDataIndicators(
-                    mHasMobileDataFeature,
-                    mShowPhoneRSSIForData ? mPhoneSignalIconId : mDataSignalIconId,
-                    mInetCondition == 0,
-                    mDataTypeIconId,
-                    mContentDescriptionPhoneSignal,
-                    mContentDescriptionDataType);
-        }
-        cluster.setIsAirplaneMode(mAirplaneMode, mAirplaneIconId);
-    }
-
-    void notifySignalsChangedCallbacks(NetworkSignalChangedCallback cb) {
-        // only show wifi in the cluster if connected or if wifi-only
-        boolean wifiEnabled = mWifiEnabled && (mWifiConnected || !mHasMobileDataFeature);
-        String wifiDesc = wifiEnabled ?
-                mWifiSsid : null;
-        boolean wifiIn = wifiEnabled && mWifiSsid != null
-                && (mWifiActivity == WifiManager.DATA_ACTIVITY_INOUT
-                || mWifiActivity == WifiManager.DATA_ACTIVITY_IN);
-        boolean wifiOut = wifiEnabled && mWifiSsid != null
-                && (mWifiActivity == WifiManager.DATA_ACTIVITY_INOUT
-                || mWifiActivity == WifiManager.DATA_ACTIVITY_OUT);
-        cb.onWifiSignalChanged(wifiEnabled, mQSWifiIconId, wifiIn, wifiOut,
-                mContentDescriptionWifi, wifiDesc);
-
-        boolean mobileIn = mDataConnected && (mDataActivity == TelephonyManager.DATA_ACTIVITY_INOUT
-                || mDataActivity == TelephonyManager.DATA_ACTIVITY_IN);
-        boolean mobileOut = mDataConnected && (mDataActivity == TelephonyManager.DATA_ACTIVITY_INOUT
-                || mDataActivity == TelephonyManager.DATA_ACTIVITY_OUT);
-        if (isEmergencyOnly()) {
-            cb.onMobileDataSignalChanged(false, mQSPhoneSignalIconId,
-                    mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut,
-                    mContentDescriptionDataType, null);
-        } else {
-            if (mIsWimaxEnabled && mWimaxConnected) {
-                // Wimax is special
-                cb.onMobileDataSignalChanged(true, mQSPhoneSignalIconId,
-                        mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut,
-                        mContentDescriptionDataType, mNetworkName);
-            } else {
-                // Normal mobile data
-                cb.onMobileDataSignalChanged(mHasMobileDataFeature, mQSPhoneSignalIconId,
-                        mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut,
-                        mContentDescriptionDataType, mNetworkName);
-            }
-        }
-        cb.onAirplaneModeChanged(mAirplaneMode);
-    }
-
-    public void setStackedMode(boolean stacked) {
-        mDataAndWifiStacked = true;
-    }
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        final String action = intent.getAction();
-        if (action.equals(WifiManager.RSSI_CHANGED_ACTION)
-                || action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)
-                || action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
-            updateWifiState(intent);
-            refreshViews();
-        } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
-            updateSimState(intent);
-            updateDataIcon();
-            refreshViews();
-        } else if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) {
-            updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false),
-                        intent.getStringExtra(TelephonyIntents.EXTRA_SPN),
-                        intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false),
-                        intent.getStringExtra(TelephonyIntents.EXTRA_PLMN));
-            refreshViews();
-        } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
-                 action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
-            updateConnectivity(intent);
-            refreshViews();
-        } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
-            refreshLocale();
-            refreshViews();
-        } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
-            refreshLocale();
-            updateAirplaneMode();
-            refreshViews();
-        } else if (action.equals(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION) ||
-                action.equals(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION) ||
-                action.equals(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION)) {
-            updateWimaxState(intent);
-            refreshViews();
-        }
-    }
-
-
-    // ===== Telephony ==============================================================
-
-    PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
-        @Override
-        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
-            if (DEBUG) {
-                Log.d(TAG, "onSignalStrengthsChanged signalStrength=" + signalStrength +
-                    ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel())));
-            }
-            mSignalStrength = signalStrength;
-            updateTelephonySignalStrength();
-            refreshViews();
-        }
-
-        @Override
-        public void onServiceStateChanged(ServiceState state) {
-            if (DEBUG) {
-                Log.d(TAG, "onServiceStateChanged voiceState=" + state.getVoiceRegState()
-                        + " dataState=" + state.getDataRegState());
-            }
-            mServiceState = state;
-            updateTelephonySignalStrength();
-            updateDataNetType();
-            updateDataIcon();
-            refreshViews();
-        }
-
-        @Override
-        public void onCallStateChanged(int state, String incomingNumber) {
-            if (DEBUG) {
-                Log.d(TAG, "onCallStateChanged state=" + state);
-            }
-            // In cdma, if a voice call is made, RSSI should switch to 1x.
-            if (isCdma()) {
-                updateTelephonySignalStrength();
-                refreshViews();
-            }
-        }
-
-        @Override
-        public void onDataConnectionStateChanged(int state, int networkType) {
-            if (DEBUG) {
-                Log.d(TAG, "onDataConnectionStateChanged: state=" + state
-                        + " type=" + networkType);
-            }
-            mDataState = state;
-            mDataNetType = networkType;
-            updateDataNetType();
-            updateDataIcon();
-            refreshViews();
-        }
-
-        @Override
-        public void onDataActivity(int direction) {
-            if (DEBUG) {
-                Log.d(TAG, "onDataActivity: direction=" + direction);
-            }
-            mDataActivity = direction;
-            updateDataIcon();
-            refreshViews();
-        }
-    };
-
-    private final void updateSimState(Intent intent) {
-        String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
-        if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
-            mSimState = IccCardConstants.State.ABSENT;
-        }
-        else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
-            mSimState = IccCardConstants.State.READY;
-        }
-        else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
-            final String lockedReason =
-                    intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON);
-            if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
-                mSimState = IccCardConstants.State.PIN_REQUIRED;
-            }
-            else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
-                mSimState = IccCardConstants.State.PUK_REQUIRED;
-            }
-            else {
-                mSimState = IccCardConstants.State.NETWORK_LOCKED;
-            }
-        } else {
-            mSimState = IccCardConstants.State.UNKNOWN;
-        }
-    }
-
-    private boolean isCdma() {
-        return (mSignalStrength != null) && !mSignalStrength.isGsm();
-    }
-
-    private boolean hasService() {
-        if (mServiceState != null) {
-            // Consider the device to be in service if either voice or data service is available.
-            // Some SIM cards are marketed as data-only and do not support voice service, and on
-            // these SIM cards, we want to show signal bars for data service as well as the "no
-            // service" or "emergency calls only" text that indicates that voice is not available.
-            switch(mServiceState.getVoiceRegState()) {
-                case ServiceState.STATE_POWER_OFF:
-                    return false;
-                case ServiceState.STATE_OUT_OF_SERVICE:
-                case ServiceState.STATE_EMERGENCY_ONLY:
-                    return mServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE;
-                default:
-                    return true;
-            }
-        } else {
-            return false;
-        }
-    }
-
-    private void updateAirplaneMode() {
-        mAirplaneMode = (Settings.Global.getInt(mContext.getContentResolver(),
-            Settings.Global.AIRPLANE_MODE_ON, 0) == 1);
-    }
-
-    private void refreshLocale() {
-        mLocale = mContext.getResources().getConfiguration().locale;
-    }
-
-    private final void updateTelephonySignalStrength() {
-        if (!hasService()) {
-            if (CHATTY) Log.d(TAG, "updateTelephonySignalStrength: !hasService()");
-            mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
-            mQSPhoneSignalIconId = R.drawable.ic_qs_signal_no_signal;
-            mDataSignalIconId = R.drawable.stat_sys_signal_null;
-        } else {
-            if (mSignalStrength == null) {
-                if (CHATTY) Log.d(TAG, "updateTelephonySignalStrength: mSignalStrength == null");
-                mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
-                mQSPhoneSignalIconId = R.drawable.ic_qs_signal_no_signal;
-                mDataSignalIconId = R.drawable.stat_sys_signal_null;
-                mContentDescriptionPhoneSignal = mContext.getString(
-                        AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0]);
-            } else {
-                int iconLevel;
-                int[] iconList;
-                if (isCdma() && mAlwaysShowCdmaRssi) {
-                    mLastSignalLevel = iconLevel = mSignalStrength.getCdmaLevel();
-                    if(DEBUG) Log.d(TAG, "mAlwaysShowCdmaRssi=" + mAlwaysShowCdmaRssi
-                            + " set to cdmaLevel=" + mSignalStrength.getCdmaLevel()
-                            + " instead of level=" + mSignalStrength.getLevel());
-                } else {
-                    mLastSignalLevel = iconLevel = mSignalStrength.getLevel();
-                }
-
-                if (isCdma()) {
-                    if (isCdmaEri()) {
-                        iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition];
-                    } else {
-                        iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition];
-                    }
-                } else {
-                    // Though mPhone is a Manager, this call is not an IPC
-                    if (mPhone.isNetworkRoaming()) {
-                        iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition];
-                    } else {
-                        iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition];
-                    }
-                }
-                mPhoneSignalIconId = iconList[iconLevel];
-                mQSPhoneSignalIconId =
-                        TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH[mInetCondition][iconLevel];
-                mContentDescriptionPhoneSignal = mContext.getString(
-                        AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[iconLevel]);
-                mDataSignalIconId = TelephonyIcons.DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel];
-            }
-        }
-    }
-
-    private final void updateDataNetType() {
-        if (mIsWimaxEnabled && mWimaxConnected) {
-            // wimax is a special 4g network not handled by telephony
-            mDataIconList = TelephonyIcons.DATA_4G[mInetCondition];
-            mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_4g;
-            mQSDataTypeIconId = TelephonyIcons.QS_DATA_4G[mInetCondition];
-            mContentDescriptionDataType = mContext.getString(
-                    R.string.accessibility_data_connection_4g);
-        } else {
-            switch (mDataNetType) {
-                case TelephonyManager.NETWORK_TYPE_UNKNOWN:
-                    if (!mShowAtLeastThreeGees) {
-                        mDataIconList = TelephonyIcons.DATA_G[mInetCondition];
-                        mDataTypeIconId = 0;
-                        mQSDataTypeIconId = 0;
-                        mContentDescriptionDataType = mContext.getString(
-                                R.string.accessibility_data_connection_gprs);
-                        break;
-                    } else {
-                        // fall through
-                    }
-                case TelephonyManager.NETWORK_TYPE_EDGE:
-                    if (!mShowAtLeastThreeGees) {
-                        mDataIconList = TelephonyIcons.DATA_E[mInetCondition];
-                        mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_e;
-                        mQSDataTypeIconId = TelephonyIcons.QS_DATA_E[mInetCondition];
-                        mContentDescriptionDataType = mContext.getString(
-                                R.string.accessibility_data_connection_edge);
-                        break;
-                    } else {
-                        // fall through
-                    }
-                case TelephonyManager.NETWORK_TYPE_UMTS:
-                    mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
-                    mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_3g;
-                    mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[mInetCondition];
-                    mContentDescriptionDataType = mContext.getString(
-                            R.string.accessibility_data_connection_3g);
-                    break;
-                case TelephonyManager.NETWORK_TYPE_HSDPA:
-                case TelephonyManager.NETWORK_TYPE_HSUPA:
-                case TelephonyManager.NETWORK_TYPE_HSPA:
-                case TelephonyManager.NETWORK_TYPE_HSPAP:
-                    if (mHspaDataDistinguishable) {
-                        mDataIconList = TelephonyIcons.DATA_H[mInetCondition];
-                        mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_h;
-                        mQSDataTypeIconId = TelephonyIcons.QS_DATA_H[mInetCondition];
-                        mContentDescriptionDataType = mContext.getString(
-                                R.string.accessibility_data_connection_3_5g);
-                    } else {
-                        mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
-                        mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_3g;
-                        mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[mInetCondition];
-                        mContentDescriptionDataType = mContext.getString(
-                                R.string.accessibility_data_connection_3g);
-                    }
-                    break;
-                case TelephonyManager.NETWORK_TYPE_CDMA:
-                    if (!mShowAtLeastThreeGees) {
-                        // display 1xRTT for IS95A/B
-                        mDataIconList = TelephonyIcons.DATA_1X[mInetCondition];
-                        mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_1x;
-                        mQSDataTypeIconId = TelephonyIcons.QS_DATA_1X[mInetCondition];
-                        mContentDescriptionDataType = mContext.getString(
-                                R.string.accessibility_data_connection_cdma);
-                        break;
-                    } else {
-                        // fall through
-                    }
-                case TelephonyManager.NETWORK_TYPE_1xRTT:
-                    if (!mShowAtLeastThreeGees) {
-                        mDataIconList = TelephonyIcons.DATA_1X[mInetCondition];
-                        mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_1x;
-                        mQSDataTypeIconId = TelephonyIcons.QS_DATA_1X[mInetCondition];
-                        mContentDescriptionDataType = mContext.getString(
-                                R.string.accessibility_data_connection_cdma);
-                        break;
-                    } else {
-                        // fall through
-                    }
-                case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
-                case TelephonyManager.NETWORK_TYPE_EVDO_A:
-                case TelephonyManager.NETWORK_TYPE_EVDO_B:
-                case TelephonyManager.NETWORK_TYPE_EHRPD:
-                    mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
-                    mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_3g;
-                    mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[mInetCondition];
-                    mContentDescriptionDataType = mContext.getString(
-                            R.string.accessibility_data_connection_3g);
-                    break;
-                case TelephonyManager.NETWORK_TYPE_LTE:
-                    boolean show4GforLTE = mContext.getResources().getBoolean(R.bool.config_show4GForLTE);
-                    if (show4GforLTE) {
-                        mDataIconList = TelephonyIcons.DATA_4G[mInetCondition];
-                        mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_4g;
-                        mQSDataTypeIconId = TelephonyIcons.QS_DATA_4G[mInetCondition];
-                        mContentDescriptionDataType = mContext.getString(
-                                R.string.accessibility_data_connection_4g);
-                    } else {
-                        mDataIconList = TelephonyIcons.DATA_LTE[mInetCondition];
-                        mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_lte;
-                        mQSDataTypeIconId = TelephonyIcons.QS_DATA_LTE[mInetCondition];
-                        mContentDescriptionDataType = mContext.getString(
-                                R.string.accessibility_data_connection_lte);
-                    }
-                    break;
-                default:
-                    if (!mShowAtLeastThreeGees) {
-                        mDataIconList = TelephonyIcons.DATA_G[mInetCondition];
-                        mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_g;
-                        mQSDataTypeIconId = TelephonyIcons.QS_DATA_G[mInetCondition];
-                        mContentDescriptionDataType = mContext.getString(
-                                R.string.accessibility_data_connection_gprs);
-                    } else {
-                        mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
-                        mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_3g;
-                        mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[mInetCondition];
-                        mContentDescriptionDataType = mContext.getString(
-                                R.string.accessibility_data_connection_3g);
-                    }
-                    break;
-            }
-        }
-
-        if (isCdma()) {
-            if (isCdmaEri()) {
-                mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam;
-                mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition];
-            }
-        } else if (mPhone.isNetworkRoaming()) {
-                mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam;
-                mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition];
-        }
-    }
-
-    boolean isCdmaEri() {
-        if (mServiceState != null) {
-            final int iconIndex = mServiceState.getCdmaEriIconIndex();
-            if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) {
-                final int iconMode = mServiceState.getCdmaEriIconMode();
-                if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL
-                        || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    private final void updateDataIcon() {
-        int iconId;
-        boolean visible = true;
-
-        if (!isCdma()) {
-            // GSM case, we have to check also the sim state
-            if (mSimState == IccCardConstants.State.READY ||
-                    mSimState == IccCardConstants.State.UNKNOWN) {
-                if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
-                    switch (mDataActivity) {
-                        case TelephonyManager.DATA_ACTIVITY_IN:
-                            iconId = mDataIconList[1];
-                            break;
-                        case TelephonyManager.DATA_ACTIVITY_OUT:
-                            iconId = mDataIconList[2];
-                            break;
-                        case TelephonyManager.DATA_ACTIVITY_INOUT:
-                            iconId = mDataIconList[3];
-                            break;
-                        default:
-                            iconId = mDataIconList[0];
-                            break;
-                    }
-                    mDataDirectionIconId = iconId;
-                } else {
-                    iconId = 0;
-                    visible = false;
-                }
-            } else {
-                iconId = R.drawable.stat_sys_no_sim;
-                visible = false; // no SIM? no data
-            }
-        } else {
-            // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT
-            if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
-                switch (mDataActivity) {
-                    case TelephonyManager.DATA_ACTIVITY_IN:
-                        iconId = mDataIconList[1];
-                        break;
-                    case TelephonyManager.DATA_ACTIVITY_OUT:
-                        iconId = mDataIconList[2];
-                        break;
-                    case TelephonyManager.DATA_ACTIVITY_INOUT:
-                        iconId = mDataIconList[3];
-                        break;
-                    case TelephonyManager.DATA_ACTIVITY_DORMANT:
-                    default:
-                        iconId = mDataIconList[0];
-                        break;
-                }
-            } else {
-                iconId = 0;
-                visible = false;
-            }
-        }
-
-        mDataDirectionIconId = iconId;
-        mDataConnected = visible;
-    }
-
-    void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
-        if (false) {
-            Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn
-                    + " showPlmn=" + showPlmn + " plmn=" + plmn);
-        }
-        StringBuilder str = new StringBuilder();
-        boolean something = false;
-        if (showPlmn && plmn != null) {
-            str.append(plmn);
-            something = true;
-        }
-        if (showSpn && spn != null) {
-            if (something) {
-                str.append(mNetworkNameSeparator);
-            }
-            str.append(spn);
-            something = true;
-        }
-        if (something) {
-            mNetworkName = str.toString();
-        } else {
-            mNetworkName = mNetworkNameDefault;
-        }
-    }
-
-    // ===== Wifi ===================================================================
-
-    class WifiHandler extends Handler {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
-                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
-                        mWifiChannel.sendMessage(Message.obtain(this,
-                                AsyncChannel.CMD_CHANNEL_FULL_CONNECTION));
-                    } else {
-                        Log.e(TAG, "Failed to connect to wifi");
-                    }
-                    break;
-                case WifiManager.DATA_ACTIVITY_NOTIFICATION:
-                    if (msg.arg1 != mWifiActivity) {
-                        mWifiActivity = msg.arg1;
-                        refreshViews();
-                    }
-                    break;
-                default:
-                    //Ignore
-                    break;
-            }
-        }
-    }
-
-    private void updateWifiState(Intent intent) {
-        final String action = intent.getAction();
-        if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
-            mWifiEnabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
-                    WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
-
-        } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
-            final NetworkInfo networkInfo = (NetworkInfo)
-                    intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-            boolean wasConnected = mWifiConnected;
-            mWifiConnected = networkInfo != null && networkInfo.isConnected();
-            // If we just connected, grab the inintial signal strength and ssid
-            if (mWifiConnected && !wasConnected) {
-                // try getting it out of the intent first
-                WifiInfo info = (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
-                if (info == null) {
-                    info = mWifiManager.getConnectionInfo();
-                }
-                if (info != null) {
-                    mWifiSsid = huntForSsid(info);
-                } else {
-                    mWifiSsid = null;
-                }
-            } else if (!mWifiConnected) {
-                mWifiSsid = null;
-            }
-        } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
-            mWifiRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
-            mWifiLevel = WifiManager.calculateSignalLevel(
-                    mWifiRssi, WifiIcons.WIFI_LEVEL_COUNT);
-        }
-
-        updateWifiIcons();
-    }
-
-    private void updateWifiIcons() {
-        if (mWifiConnected) {
-            mWifiIconId = WifiIcons.WIFI_SIGNAL_STRENGTH[mInetCondition][mWifiLevel];
-            mQSWifiIconId = WifiIcons.QS_WIFI_SIGNAL_STRENGTH[mInetCondition][mWifiLevel];
-            mContentDescriptionWifi = mContext.getString(
-                    AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[mWifiLevel]);
-        } else {
-            if (mDataAndWifiStacked) {
-                mWifiIconId = 0;
-                mQSWifiIconId = 0;
-            } else {
-                mWifiIconId = mWifiEnabled ? R.drawable.stat_sys_wifi_signal_null : 0;
-                mQSWifiIconId = mWifiEnabled ? R.drawable.ic_qs_wifi_no_network : 0;
-            }
-            mContentDescriptionWifi = mContext.getString(R.string.accessibility_no_wifi);
-        }
-    }
-
-    private String huntForSsid(WifiInfo info) {
-        String ssid = info.getSSID();
-        if (ssid != null) {
-            return ssid;
-        }
-        // OK, it's not in the connectionInfo; we have to go hunting for it
-        List<WifiConfiguration> networks = mWifiManager.getConfiguredNetworks();
-        for (WifiConfiguration net : networks) {
-            if (net.networkId == info.getNetworkId()) {
-                return net.SSID;
-            }
-        }
-        return null;
-    }
-
-
-    // ===== Wimax ===================================================================
-    private final void updateWimaxState(Intent intent) {
-        final String action = intent.getAction();
-        boolean wasConnected = mWimaxConnected;
-        if (action.equals(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION)) {
-            int wimaxStatus = intent.getIntExtra(WimaxManagerConstants.EXTRA_4G_STATE,
-                    WimaxManagerConstants.NET_4G_STATE_UNKNOWN);
-            mIsWimaxEnabled = (wimaxStatus ==
-                    WimaxManagerConstants.NET_4G_STATE_ENABLED);
-        } else if (action.equals(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION)) {
-            mWimaxSignal = intent.getIntExtra(WimaxManagerConstants.EXTRA_NEW_SIGNAL_LEVEL, 0);
-        } else if (action.equals(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION)) {
-            mWimaxState = intent.getIntExtra(WimaxManagerConstants.EXTRA_WIMAX_STATE,
-                    WimaxManagerConstants.NET_4G_STATE_UNKNOWN);
-            mWimaxExtraState = intent.getIntExtra(
-                    WimaxManagerConstants.EXTRA_WIMAX_STATE_DETAIL,
-                    WimaxManagerConstants.NET_4G_STATE_UNKNOWN);
-            mWimaxConnected = (mWimaxState ==
-                    WimaxManagerConstants.WIMAX_STATE_CONNECTED);
-            mWimaxIdle = (mWimaxExtraState == WimaxManagerConstants.WIMAX_IDLE);
-        }
-        updateDataNetType();
-        updateWimaxIcons();
-    }
-
-    private void updateWimaxIcons() {
-        if (mIsWimaxEnabled) {
-            if (mWimaxConnected) {
-                if (mWimaxIdle)
-                    mWimaxIconId = WimaxIcons.WIMAX_IDLE;
-                else
-                    mWimaxIconId = WimaxIcons.WIMAX_SIGNAL_STRENGTH[mInetCondition][mWimaxSignal];
-                mContentDescriptionWimax = mContext.getString(
-                        AccessibilityContentDescriptions.WIMAX_CONNECTION_STRENGTH[mWimaxSignal]);
-            } else {
-                mWimaxIconId = WimaxIcons.WIMAX_DISCONNECTED;
-                mContentDescriptionWimax = mContext.getString(R.string.accessibility_no_wimax);
-            }
-        } else {
-            mWimaxIconId = 0;
-        }
-    }
-
-    // ===== Full or limited Internet connectivity ==================================
-
-    private void updateConnectivity(Intent intent) {
-        if (CHATTY) {
-            Log.d(TAG, "updateConnectivity: intent=" + intent);
-        }
-
-        final ConnectivityManager connManager = (ConnectivityManager) mContext
-                .getSystemService(Context.CONNECTIVITY_SERVICE);
-        final NetworkInfo info = connManager.getActiveNetworkInfo();
-
-        // Are we connected at all, by any interface?
-        mConnected = info != null && info.isConnected();
-        if (mConnected) {
-            mConnectedNetworkType = info.getType();
-            mConnectedNetworkTypeName = info.getTypeName();
-        } else {
-            mConnectedNetworkType = ConnectivityManager.TYPE_NONE;
-            mConnectedNetworkTypeName = null;
-        }
-
-        int connectionStatus = intent.getIntExtra(ConnectivityManager.EXTRA_INET_CONDITION, 0);
-
-        if (CHATTY) {
-            Log.d(TAG, "updateConnectivity: networkInfo=" + info);
-            Log.d(TAG, "updateConnectivity: connectionStatus=" + connectionStatus);
-        }
-
-        mInetCondition = (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0);
-
-        if (info != null && info.getType() == ConnectivityManager.TYPE_BLUETOOTH) {
-            mBluetoothTethered = info.isConnected();
-        } else {
-            mBluetoothTethered = false;
-        }
-
-        // We want to update all the icons, all at once, for any condition change
-        updateDataNetType();
-        updateWimaxIcons();
-        updateDataIcon();
-        updateTelephonySignalStrength();
-        updateWifiIcons();
-    }
-
-
-    // ===== Update the views =======================================================
-
-    void refreshViews() {
-        Context context = mContext;
-
-        int combinedSignalIconId = 0;
-        String combinedLabel = "";
-        String wifiLabel = "";
-        String mobileLabel = "";
-        int N;
-        final boolean emergencyOnly = isEmergencyOnly();
-
-        if (!mHasMobileDataFeature) {
-            mDataSignalIconId = mPhoneSignalIconId = 0;
-            mQSPhoneSignalIconId = 0;
-            mobileLabel = "";
-        } else {
-            // We want to show the carrier name if in service and either:
-            //   - We are connected to mobile data, or
-            //   - We are not connected to mobile data, as long as the *reason* packets are not
-            //     being routed over that link is that we have better connectivity via wifi.
-            // If data is disconnected for some other reason but wifi (or ethernet/bluetooth)
-            // is connected, we show nothing.
-            // Otherwise (nothing connected) we show "No internet connection".
-
-            if (mDataConnected) {
-                mobileLabel = mNetworkName;
-            } else if (mConnected || emergencyOnly) {
-                if (hasService() || emergencyOnly) {
-                    // The isEmergencyOnly test covers the case of a phone with no SIM
-                    mobileLabel = mNetworkName;
-                } else {
-                    // Tablets, basically
-                    mobileLabel = "";
-                }
-            } else {
-                mobileLabel
-                    = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
-            }
-
-            // Now for things that should only be shown when actually using mobile data.
-            if (mDataConnected) {
-                combinedSignalIconId = mDataSignalIconId;
-
-                combinedLabel = mobileLabel;
-                combinedSignalIconId = mDataSignalIconId; // set by updateDataIcon()
-                mContentDescriptionCombinedSignal = mContentDescriptionDataType;
-            }
-        }
-
-        if (mWifiConnected) {
-            if (mWifiSsid == null) {
-                wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_wifi_nossid);
-            } else {
-                wifiLabel = mWifiSsid;
-                if (DEBUG) {
-                    wifiLabel += "xxxxXXXXxxxxXXXX";
-                }
-            }
-
-            combinedLabel = wifiLabel;
-            combinedSignalIconId = mWifiIconId; // set by updateWifiIcons()
-            mContentDescriptionCombinedSignal = mContentDescriptionWifi;
-        } else {
-            if (mHasMobileDataFeature) {
-                wifiLabel = "";
-            } else {
-                wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
-            }
-        }
-
-        if (mBluetoothTethered) {
-            combinedLabel = mContext.getString(R.string.bluetooth_tethered);
-            combinedSignalIconId = mBluetoothTetherIconId;
-            mContentDescriptionCombinedSignal = mContext.getString(
-                    R.string.accessibility_bluetooth_tether);
-        }
-
-        final boolean ethernetConnected = (mConnectedNetworkType == ConnectivityManager.TYPE_ETHERNET);
-        if (ethernetConnected) {
-            combinedLabel = context.getString(R.string.ethernet_label);
-        }
-
-        if (mAirplaneMode &&
-                (mServiceState == null || (!hasService() && !mServiceState.isEmergencyOnly()))) {
-            // Only display the flight-mode icon if not in "emergency calls only" mode.
-
-            // look again; your radios are now airplanes
-            mContentDescriptionPhoneSignal = mContext.getString(
-                    R.string.accessibility_airplane_mode);
-            mAirplaneIconId = FLIGHT_MODE_ICON;
-            mPhoneSignalIconId = mDataSignalIconId = mDataTypeIconId = mQSDataTypeIconId = 0;
-            mQSPhoneSignalIconId = 0;
-
-            // combined values from connected wifi take precedence over airplane mode
-            if (mWifiConnected) {
-                // Suppress "No internet connection." from mobile if wifi connected.
-                mobileLabel = "";
-            } else {
-                if (mHasMobileDataFeature) {
-                    // let the mobile icon show "No internet connection."
-                    wifiLabel = "";
-                } else {
-                    wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
-                    combinedLabel = wifiLabel;
-                }
-                mContentDescriptionCombinedSignal = mContentDescriptionPhoneSignal;
-                combinedSignalIconId = mDataSignalIconId;
-            }
-        }
-        else if (!mDataConnected && !mWifiConnected && !mBluetoothTethered && !mWimaxConnected && !ethernetConnected) {
-            // pretty much totally disconnected
-
-            combinedLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
-            // On devices without mobile radios, we want to show the wifi icon
-            combinedSignalIconId =
-                mHasMobileDataFeature ? mDataSignalIconId : mWifiIconId;
-            mContentDescriptionCombinedSignal = mHasMobileDataFeature
-                ? mContentDescriptionDataType : mContentDescriptionWifi;
-
-            mDataTypeIconId = 0;
-            mQSDataTypeIconId = 0;
-            if (isCdma()) {
-                if (isCdmaEri()) {
-                    mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam;
-                    mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition];
-                }
-            } else if (mPhone.isNetworkRoaming()) {
-                mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam;
-                mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition];
-            }
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "refreshViews connected={"
-                    + (mWifiConnected?" wifi":"")
-                    + (mDataConnected?" data":"")
-                    + " } level="
-                    + ((mSignalStrength == null)?"??":Integer.toString(mSignalStrength.getLevel()))
-                    + " combinedSignalIconId=0x"
-                    + Integer.toHexString(combinedSignalIconId)
-                    + "/" + getResourceName(combinedSignalIconId)
-                    + " mobileLabel=" + mobileLabel
-                    + " wifiLabel=" + wifiLabel
-                    + " emergencyOnly=" + emergencyOnly
-                    + " combinedLabel=" + combinedLabel
-                    + " mAirplaneMode=" + mAirplaneMode
-                    + " mDataActivity=" + mDataActivity
-                    + " mPhoneSignalIconId=0x" + Integer.toHexString(mPhoneSignalIconId)
-                    + " mQSPhoneSignalIconId=0x" + Integer.toHexString(mQSPhoneSignalIconId)
-                    + " mDataDirectionIconId=0x" + Integer.toHexString(mDataDirectionIconId)
-                    + " mDataSignalIconId=0x" + Integer.toHexString(mDataSignalIconId)
-                    + " mDataTypeIconId=0x" + Integer.toHexString(mDataTypeIconId)
-                    + " mQSDataTypeIconId=0x" + Integer.toHexString(mQSDataTypeIconId)
-                    + " mWifiIconId=0x" + Integer.toHexString(mWifiIconId)
-                    + " mQSWifiIconId=0x" + Integer.toHexString(mQSWifiIconId)
-                    + " mBluetoothTetherIconId=0x" + Integer.toHexString(mBluetoothTetherIconId));
-        }
-
-        // update QS
-        for (NetworkSignalChangedCallback cb : mSignalsChangedCallbacks) {
-            notifySignalsChangedCallbacks(cb);
-        }
-
-        if (mLastPhoneSignalIconId          != mPhoneSignalIconId
-         || mLastWifiIconId                 != mWifiIconId
-         || mLastInetCondition              != mInetCondition
-         || mLastWimaxIconId                != mWimaxIconId
-         || mLastDataTypeIconId             != mDataTypeIconId
-         || mLastAirplaneMode               != mAirplaneMode
-         || mLastLocale                     != mLocale)
-        {
-            // NB: the mLast*s will be updated later
-            for (SignalCluster cluster : mSignalClusters) {
-                refreshSignalCluster(cluster);
-            }
-        }
-
-        if (mLastAirplaneMode != mAirplaneMode) {
-            mLastAirplaneMode = mAirplaneMode;
-        }
-
-        if (mLastLocale != mLocale) {
-            mLastLocale = mLocale;
-        }
-
-        // the phone icon on phones
-        if (mLastPhoneSignalIconId != mPhoneSignalIconId) {
-            mLastPhoneSignalIconId = mPhoneSignalIconId;
-        }
-
-        // the data icon on phones
-        if (mLastDataDirectionIconId != mDataDirectionIconId) {
-            mLastDataDirectionIconId = mDataDirectionIconId;
-        }
-
-        // the wifi icon on phones
-        if (mLastWifiIconId != mWifiIconId) {
-            mLastWifiIconId = mWifiIconId;
-        }
-
-        if (mLastInetCondition != mInetCondition) {
-            mLastInetCondition = mInetCondition;
-        }
-
-        // the wimax icon on phones
-        if (mLastWimaxIconId != mWimaxIconId) {
-            mLastWimaxIconId = mWimaxIconId;
-        }
-        // the combined data signal icon
-        if (mLastCombinedSignalIconId != combinedSignalIconId) {
-            mLastCombinedSignalIconId = combinedSignalIconId;
-        }
-
-        // the data network type overlay
-        if (mLastDataTypeIconId != mDataTypeIconId) {
-            mLastDataTypeIconId = mDataTypeIconId;
-        }
-
-        // the combinedLabel in the notification panel
-        if (!mLastCombinedLabel.equals(combinedLabel)) {
-            mLastCombinedLabel = combinedLabel;
-            N = mCombinedLabelViews.size();
-            for (int i=0; i<N; i++) {
-                TextView v = mCombinedLabelViews.get(i);
-                v.setText(combinedLabel);
-            }
-        }
-
-        // wifi label
-        N = mWifiLabelViews.size();
-        for (int i=0; i<N; i++) {
-            TextView v = mWifiLabelViews.get(i);
-            v.setText(wifiLabel);
-            if ("".equals(wifiLabel)) {
-                v.setVisibility(View.GONE);
-            } else {
-                v.setVisibility(View.VISIBLE);
-            }
-        }
-
-        // mobile label
-        N = mMobileLabelViews.size();
-        for (int i=0; i<N; i++) {
-            TextView v = mMobileLabelViews.get(i);
-            v.setText(mobileLabel);
-            if ("".equals(mobileLabel)) {
-                v.setVisibility(View.GONE);
-            } else {
-                v.setVisibility(View.VISIBLE);
-            }
-        }
-
-        // e-call label
-        N = mEmergencyLabelViews.size();
-        for (int i=0; i<N; i++) {
-            TextView v = mEmergencyLabelViews.get(i);
-            if (!emergencyOnly) {
-                v.setVisibility(View.GONE);
-            } else {
-                v.setText(mobileLabel); // comes from the telephony stack
-                v.setVisibility(View.VISIBLE);
-            }
-        }
-    }
-
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("NetworkController state:");
-        pw.println(String.format("  %s network type %d (%s)",
-                mConnected?"CONNECTED":"DISCONNECTED",
-                mConnectedNetworkType, mConnectedNetworkTypeName));
-        pw.println("  - telephony ------");
-        pw.print("  hasVoiceCallingFeature()=");
-        pw.println(hasVoiceCallingFeature());
-        pw.print("  hasService()=");
-        pw.println(hasService());
-        pw.print("  mHspaDataDistinguishable=");
-        pw.println(mHspaDataDistinguishable);
-        pw.print("  mDataConnected=");
-        pw.println(mDataConnected);
-        pw.print("  mSimState=");
-        pw.println(mSimState);
-        pw.print("  mPhoneState=");
-        pw.println(mPhoneState);
-        pw.print("  mDataState=");
-        pw.println(mDataState);
-        pw.print("  mDataActivity=");
-        pw.println(mDataActivity);
-        pw.print("  mDataNetType=");
-        pw.print(mDataNetType);
-        pw.print("/");
-        pw.println(TelephonyManager.getNetworkTypeName(mDataNetType));
-        pw.print("  mServiceState=");
-        pw.println(mServiceState);
-        pw.print("  mSignalStrength=");
-        pw.println(mSignalStrength);
-        pw.print("  mLastSignalLevel=");
-        pw.println(mLastSignalLevel);
-        pw.print("  mNetworkName=");
-        pw.println(mNetworkName);
-        pw.print("  mNetworkNameDefault=");
-        pw.println(mNetworkNameDefault);
-        pw.print("  mNetworkNameSeparator=");
-        pw.println(mNetworkNameSeparator.replace("\n","\\n"));
-        pw.print("  mPhoneSignalIconId=0x");
-        pw.print(Integer.toHexString(mPhoneSignalIconId));
-        pw.print("/");
-        pw.print("  mQSPhoneSignalIconId=0x");
-        pw.print(Integer.toHexString(mQSPhoneSignalIconId));
-        pw.print("/");
-        pw.println(getResourceName(mPhoneSignalIconId));
-        pw.print("  mDataDirectionIconId=");
-        pw.print(Integer.toHexString(mDataDirectionIconId));
-        pw.print("/");
-        pw.println(getResourceName(mDataDirectionIconId));
-        pw.print("  mDataSignalIconId=");
-        pw.print(Integer.toHexString(mDataSignalIconId));
-        pw.print("/");
-        pw.println(getResourceName(mDataSignalIconId));
-        pw.print("  mDataTypeIconId=");
-        pw.print(Integer.toHexString(mDataTypeIconId));
-        pw.print("/");
-        pw.println(getResourceName(mDataTypeIconId));
-        pw.print("  mQSDataTypeIconId=");
-        pw.print(Integer.toHexString(mQSDataTypeIconId));
-        pw.print("/");
-        pw.println(getResourceName(mQSDataTypeIconId));
-
-        pw.println("  - wifi ------");
-        pw.print("  mWifiEnabled=");
-        pw.println(mWifiEnabled);
-        pw.print("  mWifiConnected=");
-        pw.println(mWifiConnected);
-        pw.print("  mWifiRssi=");
-        pw.println(mWifiRssi);
-        pw.print("  mWifiLevel=");
-        pw.println(mWifiLevel);
-        pw.print("  mWifiSsid=");
-        pw.println(mWifiSsid);
-        pw.println(String.format("  mWifiIconId=0x%08x/%s",
-                    mWifiIconId, getResourceName(mWifiIconId)));
-        pw.println(String.format("  mQSWifiIconId=0x%08x/%s",
-                    mQSWifiIconId, getResourceName(mQSWifiIconId)));
-        pw.print("  mWifiActivity=");
-        pw.println(mWifiActivity);
-
-        if (mWimaxSupported) {
-            pw.println("  - wimax ------");
-            pw.print("  mIsWimaxEnabled="); pw.println(mIsWimaxEnabled);
-            pw.print("  mWimaxConnected="); pw.println(mWimaxConnected);
-            pw.print("  mWimaxIdle="); pw.println(mWimaxIdle);
-            pw.println(String.format("  mWimaxIconId=0x%08x/%s",
-                        mWimaxIconId, getResourceName(mWimaxIconId)));
-            pw.println(String.format("  mWimaxSignal=%d", mWimaxSignal));
-            pw.println(String.format("  mWimaxState=%d", mWimaxState));
-            pw.println(String.format("  mWimaxExtraState=%d", mWimaxExtraState));
-        }
-
-        pw.println("  - Bluetooth ----");
-        pw.print("  mBtReverseTethered=");
-        pw.println(mBluetoothTethered);
-
-        pw.println("  - connectivity ------");
-        pw.print("  mInetCondition=");
-        pw.println(mInetCondition);
-
-        pw.println("  - icons ------");
-        pw.print("  mLastPhoneSignalIconId=0x");
-        pw.print(Integer.toHexString(mLastPhoneSignalIconId));
-        pw.print("/");
-        pw.println(getResourceName(mLastPhoneSignalIconId));
-        pw.print("  mLastDataDirectionIconId=0x");
-        pw.print(Integer.toHexString(mLastDataDirectionIconId));
-        pw.print("/");
-        pw.println(getResourceName(mLastDataDirectionIconId));
-        pw.print("  mLastWifiIconId=0x");
-        pw.print(Integer.toHexString(mLastWifiIconId));
-        pw.print("/");
-        pw.println(getResourceName(mLastWifiIconId));
-        pw.print("  mLastCombinedSignalIconId=0x");
-        pw.print(Integer.toHexString(mLastCombinedSignalIconId));
-        pw.print("/");
-        pw.println(getResourceName(mLastCombinedSignalIconId));
-        pw.print("  mLastDataTypeIconId=0x");
-        pw.print(Integer.toHexString(mLastDataTypeIconId));
-        pw.print("/");
-        pw.println(getResourceName(mLastDataTypeIconId));
-        pw.print("  mLastCombinedLabel=");
-        pw.print(mLastCombinedLabel);
-        pw.println("");
-    }
-
-    private String getResourceName(int resId) {
-        if (resId != 0) {
-            final Resources res = mContext.getResources();
-            try {
-                return res.getResourceName(resId);
-            } catch (android.content.res.Resources.NotFoundException ex) {
-                return "(unknown)";
-            }
-        } else {
-            return "(null)";
-        }
-    }
-
-    private boolean mDemoMode;
-    private int mDemoInetCondition;
-    private int mDemoWifiLevel;
-    private int mDemoDataTypeIconId;
-    private int mDemoMobileLevel;
-
-    @Override
-    public void dispatchDemoCommand(String command, Bundle args) {
-        if (!mDemoMode && command.equals(COMMAND_ENTER)) {
-            mDemoMode = true;
-            mDemoWifiLevel = mWifiLevel;
-            mDemoInetCondition = mInetCondition;
-            mDemoDataTypeIconId = mDataTypeIconId;
-            mDemoMobileLevel = mLastSignalLevel;
-        } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
-            mDemoMode = false;
-            for (SignalCluster cluster : mSignalClusters) {
-                refreshSignalCluster(cluster);
-            }
-        } else if (mDemoMode && command.equals(COMMAND_NETWORK)) {
-            String airplane = args.getString("airplane");
-            if (airplane != null) {
-                boolean show = airplane.equals("show");
-                for (SignalCluster cluster : mSignalClusters) {
-                    cluster.setIsAirplaneMode(show, FLIGHT_MODE_ICON);
-                }
-            }
-            String fully = args.getString("fully");
-            if (fully != null) {
-                mDemoInetCondition = Boolean.parseBoolean(fully) ? 1 : 0;
-            }
-            String wifi = args.getString("wifi");
-            if (wifi != null) {
-                boolean show = wifi.equals("show");
-                String level = args.getString("level");
-                if (level != null) {
-                    mDemoWifiLevel = level.equals("null") ? -1
-                            : Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1);
-                }
-                int iconId = mDemoWifiLevel < 0 ? R.drawable.stat_sys_wifi_signal_null
-                        : WifiIcons.WIFI_SIGNAL_STRENGTH[mDemoInetCondition][mDemoWifiLevel];
-                for (SignalCluster cluster : mSignalClusters) {
-                    cluster.setWifiIndicators(
-                            show,
-                            iconId,
-                            mDemoInetCondition == 0,
-                            "Demo");
-                }
-            }
-            String mobile = args.getString("mobile");
-            if (mobile != null) {
-                boolean show = mobile.equals("show");
-                String datatype = args.getString("datatype");
-                if (datatype != null) {
-                    mDemoDataTypeIconId =
-                            datatype.equals("1x") ? R.drawable.stat_sys_data_fully_connected_1x :
-                            datatype.equals("3g") ? R.drawable.stat_sys_data_fully_connected_3g :
-                            datatype.equals("4g") ? R.drawable.stat_sys_data_fully_connected_4g :
-                            datatype.equals("e") ? R.drawable.stat_sys_data_fully_connected_e :
-                            datatype.equals("g") ? R.drawable.stat_sys_data_fully_connected_g :
-                            datatype.equals("h") ? R.drawable.stat_sys_data_fully_connected_h :
-                            datatype.equals("lte") ? R.drawable.stat_sys_data_fully_connected_lte :
-                            datatype.equals("roam")
-                                    ? R.drawable.stat_sys_data_fully_connected_roam :
-                            0;
-                }
-                int[][] icons = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH;
-                String level = args.getString("level");
-                if (level != null) {
-                    mDemoMobileLevel = level.equals("null") ? -1
-                            : Math.min(Integer.parseInt(level), icons[0].length - 1);
-                }
-                int iconId = mDemoMobileLevel < 0 ? R.drawable.stat_sys_signal_null :
-                        icons[mDemoInetCondition][mDemoMobileLevel];
-                for (SignalCluster cluster : mSignalClusters) {
-                    cluster.setMobileDataIndicators(
-                            show,
-                            iconId,
-                            mDemoInetCondition == 0,
-                            mDemoDataTypeIconId,
-                            "Demo",
-                            "Demo");
-                }
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
new file mode 100644
index 0000000..966c0b0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -0,0 +1,1491 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.net.wimax.WimaxManagerConstants;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.provider.Settings;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.cdma.EriInfo;
+import com.android.internal.util.AsyncChannel;
+import com.android.systemui.DemoMode;
+import com.android.systemui.R;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/** Platform implementation of the network controller. **/
+public class NetworkControllerImpl extends BroadcastReceiver
+        implements NetworkController, DemoMode {
+    // debug
+    static final String TAG = "StatusBar.NetworkController";
+    static final boolean DEBUG = false;
+    static final boolean CHATTY = false; // additional diagnostics, but not logspew
+
+    private static final int FLIGHT_MODE_ICON = R.drawable.stat_sys_signal_flightmode;
+
+    // telephony
+    boolean mHspaDataDistinguishable;
+    final TelephonyManager mPhone;
+    boolean mDataConnected;
+    IccCardConstants.State mSimState = IccCardConstants.State.READY;
+    int mPhoneState = TelephonyManager.CALL_STATE_IDLE;
+    int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+    int mDataState = TelephonyManager.DATA_DISCONNECTED;
+    int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
+    ServiceState mServiceState;
+    SignalStrength mSignalStrength;
+    int[] mDataIconList = TelephonyIcons.DATA_G[0];
+    String mNetworkName;
+    String mNetworkNameDefault;
+    String mNetworkNameSeparator;
+    int mPhoneSignalIconId;
+    int mQSPhoneSignalIconId;
+    int mDataDirectionIconId; // data + data direction on phones
+    int mDataSignalIconId;
+    int mDataTypeIconId;
+    int mQSDataTypeIconId;
+    int mAirplaneIconId;
+    boolean mDataActive;
+    int mLastSignalLevel;
+    boolean mShowPhoneRSSIForData = false;
+    boolean mShowAtLeastThreeGees = false;
+    boolean mAlwaysShowCdmaRssi = false;
+
+    String mContentDescriptionPhoneSignal;
+    String mContentDescriptionWifi;
+    String mContentDescriptionWimax;
+    String mContentDescriptionCombinedSignal;
+    String mContentDescriptionDataType;
+
+    // wifi
+    final WifiManager mWifiManager;
+    AsyncChannel mWifiChannel;
+    boolean mWifiEnabled, mWifiConnected;
+    int mWifiRssi, mWifiLevel;
+    String mWifiSsid;
+    int mWifiIconId = 0;
+    int mQSWifiIconId = 0;
+    int mWifiActivity = WifiManager.DATA_ACTIVITY_NONE;
+
+    // bluetooth
+    private boolean mBluetoothTethered = false;
+    private int mBluetoothTetherIconId =
+        com.android.internal.R.drawable.stat_sys_tether_bluetooth;
+
+    //wimax
+    private boolean mWimaxSupported = false;
+    private boolean mIsWimaxEnabled = false;
+    private boolean mWimaxConnected = false;
+    private boolean mWimaxIdle = false;
+    private int mWimaxIconId = 0;
+    private int mWimaxSignal = 0;
+    private int mWimaxState = 0;
+    private int mWimaxExtraState = 0;
+
+    // data connectivity (regardless of state, can we access the internet?)
+    // state of inet connection - 0 not connected, 100 connected
+    private boolean mConnected = false;
+    private int mConnectedNetworkType = ConnectivityManager.TYPE_NONE;
+    private String mConnectedNetworkTypeName;
+    private int mInetCondition = 0;
+    private int mLastInetCondition = 0;
+    private static final int INET_CONDITION_THRESHOLD = 50;
+
+    private boolean mAirplaneMode = false;
+    private boolean mLastAirplaneMode = true;
+
+    private Locale mLocale = null;
+    private Locale mLastLocale = null;
+
+    // our ui
+    Context mContext;
+    ArrayList<TextView> mCombinedLabelViews = new ArrayList<TextView>();
+    ArrayList<TextView> mMobileLabelViews = new ArrayList<TextView>();
+    ArrayList<TextView> mWifiLabelViews = new ArrayList<TextView>();
+    ArrayList<TextView> mEmergencyLabelViews = new ArrayList<TextView>();
+    ArrayList<SignalCluster> mSignalClusters = new ArrayList<SignalCluster>();
+    ArrayList<NetworkSignalChangedCallback> mSignalsChangedCallbacks =
+            new ArrayList<NetworkSignalChangedCallback>();
+    int mLastPhoneSignalIconId = -1;
+    int mLastDataDirectionIconId = -1;
+    int mLastWifiIconId = -1;
+    int mLastWimaxIconId = -1;
+    int mLastCombinedSignalIconId = -1;
+    int mLastDataTypeIconId = -1;
+    String mLastCombinedLabel = "";
+
+    private boolean mHasMobileDataFeature;
+
+    boolean mDataAndWifiStacked = false;
+
+    public interface SignalCluster {
+        void setWifiIndicators(boolean visible, int strengthIcon, boolean problem,
+                String contentDescription);
+        void setMobileDataIndicators(boolean visible, int strengthIcon, boolean problem,
+                int typeIcon, String contentDescription, String typeContentDescription);
+        void setIsAirplaneMode(boolean is, int airplaneIcon);
+    }
+
+    /**
+     * Construct this controller object and register for updates.
+     */
+    public NetworkControllerImpl(Context context) {
+        mContext = context;
+        final Resources res = context.getResources();
+
+        ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        mHasMobileDataFeature = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+
+        mShowPhoneRSSIForData = res.getBoolean(R.bool.config_showPhoneRSSIForData);
+        mShowAtLeastThreeGees = res.getBoolean(R.bool.config_showMin3G);
+        mAlwaysShowCdmaRssi = res.getBoolean(
+                com.android.internal.R.bool.config_alwaysUseCdmaRssi);
+
+        // set up the default wifi icon, used when no radios have ever appeared
+        updateWifiIcons();
+        updateWimaxIcons();
+
+        // telephony
+        mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
+        mPhone.listen(mPhoneStateListener,
+                          PhoneStateListener.LISTEN_SERVICE_STATE
+                        | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
+                        | PhoneStateListener.LISTEN_CALL_STATE
+                        | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
+                        | PhoneStateListener.LISTEN_DATA_ACTIVITY);
+        mHspaDataDistinguishable = mContext.getResources().getBoolean(
+                R.bool.config_hspa_data_distinguishable);
+        mNetworkNameSeparator = mContext.getString(R.string.status_bar_network_name_separator);
+        mNetworkNameDefault = mContext.getString(
+                com.android.internal.R.string.lockscreen_carrier_default);
+        mNetworkName = mNetworkNameDefault;
+
+        // wifi
+        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+        Handler handler = new WifiHandler();
+        mWifiChannel = new AsyncChannel();
+        Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger();
+        if (wifiMessenger != null) {
+            mWifiChannel.connect(mContext, handler, wifiMessenger);
+        }
+
+        // broadcasts
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
+        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
+        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+        filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        mWimaxSupported = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_wimaxEnabled);
+        if(mWimaxSupported) {
+            filter.addAction(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION);
+            filter.addAction(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION);
+            filter.addAction(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION);
+        }
+        context.registerReceiver(this, filter);
+
+        // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it
+        updateAirplaneMode();
+
+        mLastLocale = mContext.getResources().getConfiguration().locale;
+    }
+
+    public boolean hasMobileDataFeature() {
+        return mHasMobileDataFeature;
+    }
+
+    public boolean hasVoiceCallingFeature() {
+        return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
+    }
+
+    public boolean isEmergencyOnly() {
+        return (mServiceState != null && mServiceState.isEmergencyOnly());
+    }
+
+    public void addCombinedLabelView(TextView v) {
+        mCombinedLabelViews.add(v);
+    }
+
+    public void addMobileLabelView(TextView v) {
+        mMobileLabelViews.add(v);
+    }
+
+    public void addWifiLabelView(TextView v) {
+        mWifiLabelViews.add(v);
+    }
+
+    public void addEmergencyLabelView(TextView v) {
+        mEmergencyLabelViews.add(v);
+    }
+
+    public void addSignalCluster(SignalCluster cluster) {
+        mSignalClusters.add(cluster);
+        refreshSignalCluster(cluster);
+    }
+
+    public void addNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) {
+        mSignalsChangedCallbacks.add(cb);
+        notifySignalsChangedCallbacks(cb);
+    }
+
+    public void removeNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) {
+        mSignalsChangedCallbacks.remove(cb);
+    }
+
+    @Override
+    public void setWifiEnabled(final boolean enabled) {
+        new AsyncTask<Void, Void, Void>() {
+            @Override
+            protected Void doInBackground(Void... args) {
+                // Disable tethering if enabling Wifi
+                final int wifiApState = mWifiManager.getWifiApState();
+                if (enabled && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) ||
+                               (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) {
+                    mWifiManager.setWifiApEnabled(null, false);
+                }
+
+                mWifiManager.setWifiEnabled(enabled);
+                return null;
+            }
+        }.execute();
+    }
+
+    public void refreshSignalCluster(SignalCluster cluster) {
+        if (mDemoMode) return;
+        cluster.setWifiIndicators(
+                // only show wifi in the cluster if connected or if wifi-only
+                mWifiEnabled && (mWifiConnected || !mHasMobileDataFeature),
+                mWifiIconId,
+                mInetCondition == 0,
+                mContentDescriptionWifi);
+
+        if (mIsWimaxEnabled && mWimaxConnected) {
+            // wimax is special
+            cluster.setMobileDataIndicators(
+                    true,
+                    mAlwaysShowCdmaRssi ? mPhoneSignalIconId : mWimaxIconId,
+                    mInetCondition == 0,
+                    mDataTypeIconId,
+                    mContentDescriptionWimax,
+                    mContentDescriptionDataType);
+        } else {
+            // normal mobile data
+            cluster.setMobileDataIndicators(
+                    mHasMobileDataFeature,
+                    mShowPhoneRSSIForData ? mPhoneSignalIconId : mDataSignalIconId,
+                    mInetCondition == 0,
+                    mDataTypeIconId,
+                    mContentDescriptionPhoneSignal,
+                    mContentDescriptionDataType);
+        }
+        cluster.setIsAirplaneMode(mAirplaneMode, mAirplaneIconId);
+    }
+
+    void notifySignalsChangedCallbacks(NetworkSignalChangedCallback cb) {
+        // only show wifi in the cluster if connected or if wifi-only
+        boolean wifiEnabled = mWifiEnabled && (mWifiConnected || !mHasMobileDataFeature);
+        String wifiDesc = wifiEnabled ?
+                mWifiSsid : null;
+        boolean wifiIn = wifiEnabled && mWifiSsid != null
+                && (mWifiActivity == WifiManager.DATA_ACTIVITY_INOUT
+                || mWifiActivity == WifiManager.DATA_ACTIVITY_IN);
+        boolean wifiOut = wifiEnabled && mWifiSsid != null
+                && (mWifiActivity == WifiManager.DATA_ACTIVITY_INOUT
+                || mWifiActivity == WifiManager.DATA_ACTIVITY_OUT);
+        cb.onWifiSignalChanged(wifiEnabled, mQSWifiIconId, wifiIn, wifiOut,
+                mContentDescriptionWifi, wifiDesc);
+
+        boolean mobileIn = mDataConnected && (mDataActivity == TelephonyManager.DATA_ACTIVITY_INOUT
+                || mDataActivity == TelephonyManager.DATA_ACTIVITY_IN);
+        boolean mobileOut = mDataConnected && (mDataActivity == TelephonyManager.DATA_ACTIVITY_INOUT
+                || mDataActivity == TelephonyManager.DATA_ACTIVITY_OUT);
+        if (isEmergencyOnly()) {
+            cb.onMobileDataSignalChanged(false, mQSPhoneSignalIconId,
+                    mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut,
+                    mContentDescriptionDataType, null);
+        } else {
+            if (mIsWimaxEnabled && mWimaxConnected) {
+                // Wimax is special
+                cb.onMobileDataSignalChanged(true, mQSPhoneSignalIconId,
+                        mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut,
+                        mContentDescriptionDataType, mNetworkName);
+            } else {
+                // Normal mobile data
+                cb.onMobileDataSignalChanged(mHasMobileDataFeature, mQSPhoneSignalIconId,
+                        mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut,
+                        mContentDescriptionDataType, mNetworkName);
+            }
+        }
+        cb.onAirplaneModeChanged(mAirplaneMode);
+    }
+
+    public void setStackedMode(boolean stacked) {
+        mDataAndWifiStacked = true;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        if (action.equals(WifiManager.RSSI_CHANGED_ACTION)
+                || action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)
+                || action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+            updateWifiState(intent);
+            refreshViews();
+        } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
+            updateSimState(intent);
+            updateDataIcon();
+            refreshViews();
+        } else if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) {
+            updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false),
+                        intent.getStringExtra(TelephonyIntents.EXTRA_SPN),
+                        intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false),
+                        intent.getStringExtra(TelephonyIntents.EXTRA_PLMN));
+            refreshViews();
+        } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
+                 action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
+            updateConnectivity(intent);
+            refreshViews();
+        } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
+            refreshLocale();
+            refreshViews();
+        } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
+            refreshLocale();
+            updateAirplaneMode();
+            refreshViews();
+        } else if (action.equals(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION) ||
+                action.equals(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION) ||
+                action.equals(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION)) {
+            updateWimaxState(intent);
+            refreshViews();
+        }
+    }
+
+
+    // ===== Telephony ==============================================================
+
+    PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+        @Override
+        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+            if (DEBUG) {
+                Log.d(TAG, "onSignalStrengthsChanged signalStrength=" + signalStrength +
+                    ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel())));
+            }
+            mSignalStrength = signalStrength;
+            updateTelephonySignalStrength();
+            refreshViews();
+        }
+
+        @Override
+        public void onServiceStateChanged(ServiceState state) {
+            if (DEBUG) {
+                Log.d(TAG, "onServiceStateChanged voiceState=" + state.getVoiceRegState()
+                        + " dataState=" + state.getDataRegState());
+            }
+            mServiceState = state;
+            updateTelephonySignalStrength();
+            updateDataNetType();
+            updateDataIcon();
+            refreshViews();
+        }
+
+        @Override
+        public void onCallStateChanged(int state, String incomingNumber) {
+            if (DEBUG) {
+                Log.d(TAG, "onCallStateChanged state=" + state);
+            }
+            // In cdma, if a voice call is made, RSSI should switch to 1x.
+            if (isCdma()) {
+                updateTelephonySignalStrength();
+                refreshViews();
+            }
+        }
+
+        @Override
+        public void onDataConnectionStateChanged(int state, int networkType) {
+            if (DEBUG) {
+                Log.d(TAG, "onDataConnectionStateChanged: state=" + state
+                        + " type=" + networkType);
+            }
+            mDataState = state;
+            mDataNetType = networkType;
+            updateDataNetType();
+            updateDataIcon();
+            refreshViews();
+        }
+
+        @Override
+        public void onDataActivity(int direction) {
+            if (DEBUG) {
+                Log.d(TAG, "onDataActivity: direction=" + direction);
+            }
+            mDataActivity = direction;
+            updateDataIcon();
+            refreshViews();
+        }
+    };
+
+    private final void updateSimState(Intent intent) {
+        String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
+        if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
+            mSimState = IccCardConstants.State.ABSENT;
+        }
+        else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
+            mSimState = IccCardConstants.State.READY;
+        }
+        else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
+            final String lockedReason =
+                    intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON);
+            if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
+                mSimState = IccCardConstants.State.PIN_REQUIRED;
+            }
+            else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
+                mSimState = IccCardConstants.State.PUK_REQUIRED;
+            }
+            else {
+                mSimState = IccCardConstants.State.NETWORK_LOCKED;
+            }
+        } else {
+            mSimState = IccCardConstants.State.UNKNOWN;
+        }
+    }
+
+    private boolean isCdma() {
+        return (mSignalStrength != null) && !mSignalStrength.isGsm();
+    }
+
+    private boolean hasService() {
+        if (mServiceState != null) {
+            // Consider the device to be in service if either voice or data service is available.
+            // Some SIM cards are marketed as data-only and do not support voice service, and on
+            // these SIM cards, we want to show signal bars for data service as well as the "no
+            // service" or "emergency calls only" text that indicates that voice is not available.
+            switch(mServiceState.getVoiceRegState()) {
+                case ServiceState.STATE_POWER_OFF:
+                    return false;
+                case ServiceState.STATE_OUT_OF_SERVICE:
+                case ServiceState.STATE_EMERGENCY_ONLY:
+                    return mServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE;
+                default:
+                    return true;
+            }
+        } else {
+            return false;
+        }
+    }
+
+    private void updateAirplaneMode() {
+        mAirplaneMode = (Settings.Global.getInt(mContext.getContentResolver(),
+            Settings.Global.AIRPLANE_MODE_ON, 0) == 1);
+    }
+
+    private void refreshLocale() {
+        mLocale = mContext.getResources().getConfiguration().locale;
+    }
+
+    private final void updateTelephonySignalStrength() {
+        if (!hasService()) {
+            if (CHATTY) Log.d(TAG, "updateTelephonySignalStrength: !hasService()");
+            mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
+            mQSPhoneSignalIconId = R.drawable.ic_qs_signal_no_signal;
+            mDataSignalIconId = R.drawable.stat_sys_signal_null;
+        } else {
+            if (mSignalStrength == null) {
+                if (CHATTY) Log.d(TAG, "updateTelephonySignalStrength: mSignalStrength == null");
+                mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
+                mQSPhoneSignalIconId = R.drawable.ic_qs_signal_no_signal;
+                mDataSignalIconId = R.drawable.stat_sys_signal_null;
+                mContentDescriptionPhoneSignal = mContext.getString(
+                        AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0]);
+            } else {
+                int iconLevel;
+                int[] iconList;
+                if (isCdma() && mAlwaysShowCdmaRssi) {
+                    mLastSignalLevel = iconLevel = mSignalStrength.getCdmaLevel();
+                    if(DEBUG) Log.d(TAG, "mAlwaysShowCdmaRssi=" + mAlwaysShowCdmaRssi
+                            + " set to cdmaLevel=" + mSignalStrength.getCdmaLevel()
+                            + " instead of level=" + mSignalStrength.getLevel());
+                } else {
+                    mLastSignalLevel = iconLevel = mSignalStrength.getLevel();
+                }
+
+                if (isCdma()) {
+                    if (isCdmaEri()) {
+                        iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition];
+                    } else {
+                        iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition];
+                    }
+                } else {
+                    // Though mPhone is a Manager, this call is not an IPC
+                    if (mPhone.isNetworkRoaming()) {
+                        iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition];
+                    } else {
+                        iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition];
+                    }
+                }
+                mPhoneSignalIconId = iconList[iconLevel];
+                mQSPhoneSignalIconId =
+                        TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH[mInetCondition][iconLevel];
+                mContentDescriptionPhoneSignal = mContext.getString(
+                        AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[iconLevel]);
+                mDataSignalIconId = TelephonyIcons.DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel];
+            }
+        }
+    }
+
+    private final void updateDataNetType() {
+        if (mIsWimaxEnabled && mWimaxConnected) {
+            // wimax is a special 4g network not handled by telephony
+            mDataIconList = TelephonyIcons.DATA_4G[mInetCondition];
+            mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_4g;
+            mQSDataTypeIconId = TelephonyIcons.QS_DATA_4G[mInetCondition];
+            mContentDescriptionDataType = mContext.getString(
+                    R.string.accessibility_data_connection_4g);
+        } else {
+            switch (mDataNetType) {
+                case TelephonyManager.NETWORK_TYPE_UNKNOWN:
+                    if (!mShowAtLeastThreeGees) {
+                        mDataIconList = TelephonyIcons.DATA_G[mInetCondition];
+                        mDataTypeIconId = 0;
+                        mQSDataTypeIconId = 0;
+                        mContentDescriptionDataType = mContext.getString(
+                                R.string.accessibility_data_connection_gprs);
+                        break;
+                    } else {
+                        // fall through
+                    }
+                case TelephonyManager.NETWORK_TYPE_EDGE:
+                    if (!mShowAtLeastThreeGees) {
+                        mDataIconList = TelephonyIcons.DATA_E[mInetCondition];
+                        mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_e;
+                        mQSDataTypeIconId = TelephonyIcons.QS_DATA_E[mInetCondition];
+                        mContentDescriptionDataType = mContext.getString(
+                                R.string.accessibility_data_connection_edge);
+                        break;
+                    } else {
+                        // fall through
+                    }
+                case TelephonyManager.NETWORK_TYPE_UMTS:
+                    mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
+                    mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_3g;
+                    mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[mInetCondition];
+                    mContentDescriptionDataType = mContext.getString(
+                            R.string.accessibility_data_connection_3g);
+                    break;
+                case TelephonyManager.NETWORK_TYPE_HSDPA:
+                case TelephonyManager.NETWORK_TYPE_HSUPA:
+                case TelephonyManager.NETWORK_TYPE_HSPA:
+                case TelephonyManager.NETWORK_TYPE_HSPAP:
+                    if (mHspaDataDistinguishable) {
+                        mDataIconList = TelephonyIcons.DATA_H[mInetCondition];
+                        mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_h;
+                        mQSDataTypeIconId = TelephonyIcons.QS_DATA_H[mInetCondition];
+                        mContentDescriptionDataType = mContext.getString(
+                                R.string.accessibility_data_connection_3_5g);
+                    } else {
+                        mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
+                        mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_3g;
+                        mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[mInetCondition];
+                        mContentDescriptionDataType = mContext.getString(
+                                R.string.accessibility_data_connection_3g);
+                    }
+                    break;
+                case TelephonyManager.NETWORK_TYPE_CDMA:
+                    if (!mShowAtLeastThreeGees) {
+                        // display 1xRTT for IS95A/B
+                        mDataIconList = TelephonyIcons.DATA_1X[mInetCondition];
+                        mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_1x;
+                        mQSDataTypeIconId = TelephonyIcons.QS_DATA_1X[mInetCondition];
+                        mContentDescriptionDataType = mContext.getString(
+                                R.string.accessibility_data_connection_cdma);
+                        break;
+                    } else {
+                        // fall through
+                    }
+                case TelephonyManager.NETWORK_TYPE_1xRTT:
+                    if (!mShowAtLeastThreeGees) {
+                        mDataIconList = TelephonyIcons.DATA_1X[mInetCondition];
+                        mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_1x;
+                        mQSDataTypeIconId = TelephonyIcons.QS_DATA_1X[mInetCondition];
+                        mContentDescriptionDataType = mContext.getString(
+                                R.string.accessibility_data_connection_cdma);
+                        break;
+                    } else {
+                        // fall through
+                    }
+                case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
+                case TelephonyManager.NETWORK_TYPE_EVDO_A:
+                case TelephonyManager.NETWORK_TYPE_EVDO_B:
+                case TelephonyManager.NETWORK_TYPE_EHRPD:
+                    mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
+                    mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_3g;
+                    mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[mInetCondition];
+                    mContentDescriptionDataType = mContext.getString(
+                            R.string.accessibility_data_connection_3g);
+                    break;
+                case TelephonyManager.NETWORK_TYPE_LTE:
+                    boolean show4GforLTE = mContext.getResources().getBoolean(R.bool.config_show4GForLTE);
+                    if (show4GforLTE) {
+                        mDataIconList = TelephonyIcons.DATA_4G[mInetCondition];
+                        mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_4g;
+                        mQSDataTypeIconId = TelephonyIcons.QS_DATA_4G[mInetCondition];
+                        mContentDescriptionDataType = mContext.getString(
+                                R.string.accessibility_data_connection_4g);
+                    } else {
+                        mDataIconList = TelephonyIcons.DATA_LTE[mInetCondition];
+                        mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_lte;
+                        mQSDataTypeIconId = TelephonyIcons.QS_DATA_LTE[mInetCondition];
+                        mContentDescriptionDataType = mContext.getString(
+                                R.string.accessibility_data_connection_lte);
+                    }
+                    break;
+                default:
+                    if (!mShowAtLeastThreeGees) {
+                        mDataIconList = TelephonyIcons.DATA_G[mInetCondition];
+                        mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_g;
+                        mQSDataTypeIconId = TelephonyIcons.QS_DATA_G[mInetCondition];
+                        mContentDescriptionDataType = mContext.getString(
+                                R.string.accessibility_data_connection_gprs);
+                    } else {
+                        mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
+                        mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_3g;
+                        mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[mInetCondition];
+                        mContentDescriptionDataType = mContext.getString(
+                                R.string.accessibility_data_connection_3g);
+                    }
+                    break;
+            }
+        }
+
+        if (isCdma()) {
+            if (isCdmaEri()) {
+                mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam;
+                mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition];
+            }
+        } else if (mPhone.isNetworkRoaming()) {
+                mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam;
+                mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition];
+        }
+    }
+
+    boolean isCdmaEri() {
+        if (mServiceState != null) {
+            final int iconIndex = mServiceState.getCdmaEriIconIndex();
+            if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) {
+                final int iconMode = mServiceState.getCdmaEriIconMode();
+                if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL
+                        || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private final void updateDataIcon() {
+        int iconId;
+        boolean visible = true;
+
+        if (!isCdma()) {
+            // GSM case, we have to check also the sim state
+            if (mSimState == IccCardConstants.State.READY ||
+                    mSimState == IccCardConstants.State.UNKNOWN) {
+                if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
+                    switch (mDataActivity) {
+                        case TelephonyManager.DATA_ACTIVITY_IN:
+                            iconId = mDataIconList[1];
+                            break;
+                        case TelephonyManager.DATA_ACTIVITY_OUT:
+                            iconId = mDataIconList[2];
+                            break;
+                        case TelephonyManager.DATA_ACTIVITY_INOUT:
+                            iconId = mDataIconList[3];
+                            break;
+                        default:
+                            iconId = mDataIconList[0];
+                            break;
+                    }
+                    mDataDirectionIconId = iconId;
+                } else {
+                    iconId = 0;
+                    visible = false;
+                }
+            } else {
+                iconId = R.drawable.stat_sys_no_sim;
+                visible = false; // no SIM? no data
+            }
+        } else {
+            // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT
+            if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
+                switch (mDataActivity) {
+                    case TelephonyManager.DATA_ACTIVITY_IN:
+                        iconId = mDataIconList[1];
+                        break;
+                    case TelephonyManager.DATA_ACTIVITY_OUT:
+                        iconId = mDataIconList[2];
+                        break;
+                    case TelephonyManager.DATA_ACTIVITY_INOUT:
+                        iconId = mDataIconList[3];
+                        break;
+                    case TelephonyManager.DATA_ACTIVITY_DORMANT:
+                    default:
+                        iconId = mDataIconList[0];
+                        break;
+                }
+            } else {
+                iconId = 0;
+                visible = false;
+            }
+        }
+
+        mDataDirectionIconId = iconId;
+        mDataConnected = visible;
+    }
+
+    void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
+        if (false) {
+            Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn
+                    + " showPlmn=" + showPlmn + " plmn=" + plmn);
+        }
+        StringBuilder str = new StringBuilder();
+        boolean something = false;
+        if (showPlmn && plmn != null) {
+            str.append(plmn);
+            something = true;
+        }
+        if (showSpn && spn != null) {
+            if (something) {
+                str.append(mNetworkNameSeparator);
+            }
+            str.append(spn);
+            something = true;
+        }
+        if (something) {
+            mNetworkName = str.toString();
+        } else {
+            mNetworkName = mNetworkNameDefault;
+        }
+    }
+
+    // ===== Wifi ===================================================================
+
+    class WifiHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+                        mWifiChannel.sendMessage(Message.obtain(this,
+                                AsyncChannel.CMD_CHANNEL_FULL_CONNECTION));
+                    } else {
+                        Log.e(TAG, "Failed to connect to wifi");
+                    }
+                    break;
+                case WifiManager.DATA_ACTIVITY_NOTIFICATION:
+                    if (msg.arg1 != mWifiActivity) {
+                        mWifiActivity = msg.arg1;
+                        refreshViews();
+                    }
+                    break;
+                default:
+                    //Ignore
+                    break;
+            }
+        }
+    }
+
+    private void updateWifiState(Intent intent) {
+        final String action = intent.getAction();
+        if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+            mWifiEnabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+                    WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
+
+        } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+            final NetworkInfo networkInfo = (NetworkInfo)
+                    intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
+            boolean wasConnected = mWifiConnected;
+            mWifiConnected = networkInfo != null && networkInfo.isConnected();
+            // If we just connected, grab the inintial signal strength and ssid
+            if (mWifiConnected && !wasConnected) {
+                // try getting it out of the intent first
+                WifiInfo info = (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
+                if (info == null) {
+                    info = mWifiManager.getConnectionInfo();
+                }
+                if (info != null) {
+                    mWifiSsid = huntForSsid(info);
+                } else {
+                    mWifiSsid = null;
+                }
+            } else if (!mWifiConnected) {
+                mWifiSsid = null;
+            }
+        } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
+            mWifiRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
+            mWifiLevel = WifiManager.calculateSignalLevel(
+                    mWifiRssi, WifiIcons.WIFI_LEVEL_COUNT);
+        }
+
+        updateWifiIcons();
+    }
+
+    private void updateWifiIcons() {
+        if (mWifiConnected) {
+            mWifiIconId = WifiIcons.WIFI_SIGNAL_STRENGTH[mInetCondition][mWifiLevel];
+            mQSWifiIconId = WifiIcons.QS_WIFI_SIGNAL_STRENGTH[mInetCondition][mWifiLevel];
+            mContentDescriptionWifi = mContext.getString(
+                    AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[mWifiLevel]);
+        } else {
+            if (mDataAndWifiStacked) {
+                mWifiIconId = 0;
+                mQSWifiIconId = 0;
+            } else {
+                mWifiIconId = mWifiEnabled ? R.drawable.stat_sys_wifi_signal_null : 0;
+                mQSWifiIconId = mWifiEnabled ? R.drawable.ic_qs_wifi_no_network : 0;
+            }
+            mContentDescriptionWifi = mContext.getString(R.string.accessibility_no_wifi);
+        }
+    }
+
+    private String huntForSsid(WifiInfo info) {
+        String ssid = info.getSSID();
+        if (ssid != null) {
+            return ssid;
+        }
+        // OK, it's not in the connectionInfo; we have to go hunting for it
+        List<WifiConfiguration> networks = mWifiManager.getConfiguredNetworks();
+        for (WifiConfiguration net : networks) {
+            if (net.networkId == info.getNetworkId()) {
+                return net.SSID;
+            }
+        }
+        return null;
+    }
+
+
+    // ===== Wimax ===================================================================
+    private final void updateWimaxState(Intent intent) {
+        final String action = intent.getAction();
+        boolean wasConnected = mWimaxConnected;
+        if (action.equals(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION)) {
+            int wimaxStatus = intent.getIntExtra(WimaxManagerConstants.EXTRA_4G_STATE,
+                    WimaxManagerConstants.NET_4G_STATE_UNKNOWN);
+            mIsWimaxEnabled = (wimaxStatus ==
+                    WimaxManagerConstants.NET_4G_STATE_ENABLED);
+        } else if (action.equals(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION)) {
+            mWimaxSignal = intent.getIntExtra(WimaxManagerConstants.EXTRA_NEW_SIGNAL_LEVEL, 0);
+        } else if (action.equals(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION)) {
+            mWimaxState = intent.getIntExtra(WimaxManagerConstants.EXTRA_WIMAX_STATE,
+                    WimaxManagerConstants.NET_4G_STATE_UNKNOWN);
+            mWimaxExtraState = intent.getIntExtra(
+                    WimaxManagerConstants.EXTRA_WIMAX_STATE_DETAIL,
+                    WimaxManagerConstants.NET_4G_STATE_UNKNOWN);
+            mWimaxConnected = (mWimaxState ==
+                    WimaxManagerConstants.WIMAX_STATE_CONNECTED);
+            mWimaxIdle = (mWimaxExtraState == WimaxManagerConstants.WIMAX_IDLE);
+        }
+        updateDataNetType();
+        updateWimaxIcons();
+    }
+
+    private void updateWimaxIcons() {
+        if (mIsWimaxEnabled) {
+            if (mWimaxConnected) {
+                if (mWimaxIdle)
+                    mWimaxIconId = WimaxIcons.WIMAX_IDLE;
+                else
+                    mWimaxIconId = WimaxIcons.WIMAX_SIGNAL_STRENGTH[mInetCondition][mWimaxSignal];
+                mContentDescriptionWimax = mContext.getString(
+                        AccessibilityContentDescriptions.WIMAX_CONNECTION_STRENGTH[mWimaxSignal]);
+            } else {
+                mWimaxIconId = WimaxIcons.WIMAX_DISCONNECTED;
+                mContentDescriptionWimax = mContext.getString(R.string.accessibility_no_wimax);
+            }
+        } else {
+            mWimaxIconId = 0;
+        }
+    }
+
+    // ===== Full or limited Internet connectivity ==================================
+
+    private void updateConnectivity(Intent intent) {
+        if (CHATTY) {
+            Log.d(TAG, "updateConnectivity: intent=" + intent);
+        }
+
+        final ConnectivityManager connManager = (ConnectivityManager) mContext
+                .getSystemService(Context.CONNECTIVITY_SERVICE);
+        final NetworkInfo info = connManager.getActiveNetworkInfo();
+
+        // Are we connected at all, by any interface?
+        mConnected = info != null && info.isConnected();
+        if (mConnected) {
+            mConnectedNetworkType = info.getType();
+            mConnectedNetworkTypeName = info.getTypeName();
+        } else {
+            mConnectedNetworkType = ConnectivityManager.TYPE_NONE;
+            mConnectedNetworkTypeName = null;
+        }
+
+        int connectionStatus = intent.getIntExtra(ConnectivityManager.EXTRA_INET_CONDITION, 0);
+
+        if (CHATTY) {
+            Log.d(TAG, "updateConnectivity: networkInfo=" + info);
+            Log.d(TAG, "updateConnectivity: connectionStatus=" + connectionStatus);
+        }
+
+        mInetCondition = (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0);
+
+        if (info != null && info.getType() == ConnectivityManager.TYPE_BLUETOOTH) {
+            mBluetoothTethered = info.isConnected();
+        } else {
+            mBluetoothTethered = false;
+        }
+
+        // We want to update all the icons, all at once, for any condition change
+        updateDataNetType();
+        updateWimaxIcons();
+        updateDataIcon();
+        updateTelephonySignalStrength();
+        updateWifiIcons();
+    }
+
+
+    // ===== Update the views =======================================================
+
+    void refreshViews() {
+        Context context = mContext;
+
+        int combinedSignalIconId = 0;
+        String combinedLabel = "";
+        String wifiLabel = "";
+        String mobileLabel = "";
+        int N;
+        final boolean emergencyOnly = isEmergencyOnly();
+
+        if (!mHasMobileDataFeature) {
+            mDataSignalIconId = mPhoneSignalIconId = 0;
+            mQSPhoneSignalIconId = 0;
+            mobileLabel = "";
+        } else {
+            // We want to show the carrier name if in service and either:
+            //   - We are connected to mobile data, or
+            //   - We are not connected to mobile data, as long as the *reason* packets are not
+            //     being routed over that link is that we have better connectivity via wifi.
+            // If data is disconnected for some other reason but wifi (or ethernet/bluetooth)
+            // is connected, we show nothing.
+            // Otherwise (nothing connected) we show "No internet connection".
+
+            if (mDataConnected) {
+                mobileLabel = mNetworkName;
+            } else if (mConnected || emergencyOnly) {
+                if (hasService() || emergencyOnly) {
+                    // The isEmergencyOnly test covers the case of a phone with no SIM
+                    mobileLabel = mNetworkName;
+                } else {
+                    // Tablets, basically
+                    mobileLabel = "";
+                }
+            } else {
+                mobileLabel
+                    = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
+            }
+
+            // Now for things that should only be shown when actually using mobile data.
+            if (mDataConnected) {
+                combinedSignalIconId = mDataSignalIconId;
+
+                combinedLabel = mobileLabel;
+                combinedSignalIconId = mDataSignalIconId; // set by updateDataIcon()
+                mContentDescriptionCombinedSignal = mContentDescriptionDataType;
+            }
+        }
+
+        if (mWifiConnected) {
+            if (mWifiSsid == null) {
+                wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_wifi_nossid);
+            } else {
+                wifiLabel = mWifiSsid;
+                if (DEBUG) {
+                    wifiLabel += "xxxxXXXXxxxxXXXX";
+                }
+            }
+
+            combinedLabel = wifiLabel;
+            combinedSignalIconId = mWifiIconId; // set by updateWifiIcons()
+            mContentDescriptionCombinedSignal = mContentDescriptionWifi;
+        } else {
+            if (mHasMobileDataFeature) {
+                wifiLabel = "";
+            } else {
+                wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
+            }
+        }
+
+        if (mBluetoothTethered) {
+            combinedLabel = mContext.getString(R.string.bluetooth_tethered);
+            combinedSignalIconId = mBluetoothTetherIconId;
+            mContentDescriptionCombinedSignal = mContext.getString(
+                    R.string.accessibility_bluetooth_tether);
+        }
+
+        final boolean ethernetConnected = (mConnectedNetworkType == ConnectivityManager.TYPE_ETHERNET);
+        if (ethernetConnected) {
+            combinedLabel = context.getString(R.string.ethernet_label);
+        }
+
+        if (mAirplaneMode &&
+                (mServiceState == null || (!hasService() && !mServiceState.isEmergencyOnly()))) {
+            // Only display the flight-mode icon if not in "emergency calls only" mode.
+
+            // look again; your radios are now airplanes
+            mContentDescriptionPhoneSignal = mContext.getString(
+                    R.string.accessibility_airplane_mode);
+            mAirplaneIconId = FLIGHT_MODE_ICON;
+            mPhoneSignalIconId = mDataSignalIconId = mDataTypeIconId = mQSDataTypeIconId = 0;
+            mQSPhoneSignalIconId = 0;
+
+            // combined values from connected wifi take precedence over airplane mode
+            if (mWifiConnected) {
+                // Suppress "No internet connection." from mobile if wifi connected.
+                mobileLabel = "";
+            } else {
+                if (mHasMobileDataFeature) {
+                    // let the mobile icon show "No internet connection."
+                    wifiLabel = "";
+                } else {
+                    wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
+                    combinedLabel = wifiLabel;
+                }
+                mContentDescriptionCombinedSignal = mContentDescriptionPhoneSignal;
+                combinedSignalIconId = mDataSignalIconId;
+            }
+        }
+        else if (!mDataConnected && !mWifiConnected && !mBluetoothTethered && !mWimaxConnected && !ethernetConnected) {
+            // pretty much totally disconnected
+
+            combinedLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
+            // On devices without mobile radios, we want to show the wifi icon
+            combinedSignalIconId =
+                mHasMobileDataFeature ? mDataSignalIconId : mWifiIconId;
+            mContentDescriptionCombinedSignal = mHasMobileDataFeature
+                ? mContentDescriptionDataType : mContentDescriptionWifi;
+
+            mDataTypeIconId = 0;
+            mQSDataTypeIconId = 0;
+            if (isCdma()) {
+                if (isCdmaEri()) {
+                    mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam;
+                    mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition];
+                }
+            } else if (mPhone.isNetworkRoaming()) {
+                mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam;
+                mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition];
+            }
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "refreshViews connected={"
+                    + (mWifiConnected?" wifi":"")
+                    + (mDataConnected?" data":"")
+                    + " } level="
+                    + ((mSignalStrength == null)?"??":Integer.toString(mSignalStrength.getLevel()))
+                    + " combinedSignalIconId=0x"
+                    + Integer.toHexString(combinedSignalIconId)
+                    + "/" + getResourceName(combinedSignalIconId)
+                    + " mobileLabel=" + mobileLabel
+                    + " wifiLabel=" + wifiLabel
+                    + " emergencyOnly=" + emergencyOnly
+                    + " combinedLabel=" + combinedLabel
+                    + " mAirplaneMode=" + mAirplaneMode
+                    + " mDataActivity=" + mDataActivity
+                    + " mPhoneSignalIconId=0x" + Integer.toHexString(mPhoneSignalIconId)
+                    + " mQSPhoneSignalIconId=0x" + Integer.toHexString(mQSPhoneSignalIconId)
+                    + " mDataDirectionIconId=0x" + Integer.toHexString(mDataDirectionIconId)
+                    + " mDataSignalIconId=0x" + Integer.toHexString(mDataSignalIconId)
+                    + " mDataTypeIconId=0x" + Integer.toHexString(mDataTypeIconId)
+                    + " mQSDataTypeIconId=0x" + Integer.toHexString(mQSDataTypeIconId)
+                    + " mWifiIconId=0x" + Integer.toHexString(mWifiIconId)
+                    + " mQSWifiIconId=0x" + Integer.toHexString(mQSWifiIconId)
+                    + " mBluetoothTetherIconId=0x" + Integer.toHexString(mBluetoothTetherIconId));
+        }
+
+        // update QS
+        for (NetworkSignalChangedCallback cb : mSignalsChangedCallbacks) {
+            notifySignalsChangedCallbacks(cb);
+        }
+
+        if (mLastPhoneSignalIconId          != mPhoneSignalIconId
+         || mLastWifiIconId                 != mWifiIconId
+         || mLastInetCondition              != mInetCondition
+         || mLastWimaxIconId                != mWimaxIconId
+         || mLastDataTypeIconId             != mDataTypeIconId
+         || mLastAirplaneMode               != mAirplaneMode
+         || mLastLocale                     != mLocale)
+        {
+            // NB: the mLast*s will be updated later
+            for (SignalCluster cluster : mSignalClusters) {
+                refreshSignalCluster(cluster);
+            }
+        }
+
+        if (mLastAirplaneMode != mAirplaneMode) {
+            mLastAirplaneMode = mAirplaneMode;
+        }
+
+        if (mLastLocale != mLocale) {
+            mLastLocale = mLocale;
+        }
+
+        // the phone icon on phones
+        if (mLastPhoneSignalIconId != mPhoneSignalIconId) {
+            mLastPhoneSignalIconId = mPhoneSignalIconId;
+        }
+
+        // the data icon on phones
+        if (mLastDataDirectionIconId != mDataDirectionIconId) {
+            mLastDataDirectionIconId = mDataDirectionIconId;
+        }
+
+        // the wifi icon on phones
+        if (mLastWifiIconId != mWifiIconId) {
+            mLastWifiIconId = mWifiIconId;
+        }
+
+        if (mLastInetCondition != mInetCondition) {
+            mLastInetCondition = mInetCondition;
+        }
+
+        // the wimax icon on phones
+        if (mLastWimaxIconId != mWimaxIconId) {
+            mLastWimaxIconId = mWimaxIconId;
+        }
+        // the combined data signal icon
+        if (mLastCombinedSignalIconId != combinedSignalIconId) {
+            mLastCombinedSignalIconId = combinedSignalIconId;
+        }
+
+        // the data network type overlay
+        if (mLastDataTypeIconId != mDataTypeIconId) {
+            mLastDataTypeIconId = mDataTypeIconId;
+        }
+
+        // the combinedLabel in the notification panel
+        if (!mLastCombinedLabel.equals(combinedLabel)) {
+            mLastCombinedLabel = combinedLabel;
+            N = mCombinedLabelViews.size();
+            for (int i=0; i<N; i++) {
+                TextView v = mCombinedLabelViews.get(i);
+                v.setText(combinedLabel);
+            }
+        }
+
+        // wifi label
+        N = mWifiLabelViews.size();
+        for (int i=0; i<N; i++) {
+            TextView v = mWifiLabelViews.get(i);
+            v.setText(wifiLabel);
+            if ("".equals(wifiLabel)) {
+                v.setVisibility(View.GONE);
+            } else {
+                v.setVisibility(View.VISIBLE);
+            }
+        }
+
+        // mobile label
+        N = mMobileLabelViews.size();
+        for (int i=0; i<N; i++) {
+            TextView v = mMobileLabelViews.get(i);
+            v.setText(mobileLabel);
+            if ("".equals(mobileLabel)) {
+                v.setVisibility(View.GONE);
+            } else {
+                v.setVisibility(View.VISIBLE);
+            }
+        }
+
+        // e-call label
+        N = mEmergencyLabelViews.size();
+        for (int i=0; i<N; i++) {
+            TextView v = mEmergencyLabelViews.get(i);
+            if (!emergencyOnly) {
+                v.setVisibility(View.GONE);
+            } else {
+                v.setText(mobileLabel); // comes from the telephony stack
+                v.setVisibility(View.VISIBLE);
+            }
+        }
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("NetworkController state:");
+        pw.println(String.format("  %s network type %d (%s)",
+                mConnected?"CONNECTED":"DISCONNECTED",
+                mConnectedNetworkType, mConnectedNetworkTypeName));
+        pw.println("  - telephony ------");
+        pw.print("  hasVoiceCallingFeature()=");
+        pw.println(hasVoiceCallingFeature());
+        pw.print("  hasService()=");
+        pw.println(hasService());
+        pw.print("  mHspaDataDistinguishable=");
+        pw.println(mHspaDataDistinguishable);
+        pw.print("  mDataConnected=");
+        pw.println(mDataConnected);
+        pw.print("  mSimState=");
+        pw.println(mSimState);
+        pw.print("  mPhoneState=");
+        pw.println(mPhoneState);
+        pw.print("  mDataState=");
+        pw.println(mDataState);
+        pw.print("  mDataActivity=");
+        pw.println(mDataActivity);
+        pw.print("  mDataNetType=");
+        pw.print(mDataNetType);
+        pw.print("/");
+        pw.println(TelephonyManager.getNetworkTypeName(mDataNetType));
+        pw.print("  mServiceState=");
+        pw.println(mServiceState);
+        pw.print("  mSignalStrength=");
+        pw.println(mSignalStrength);
+        pw.print("  mLastSignalLevel=");
+        pw.println(mLastSignalLevel);
+        pw.print("  mNetworkName=");
+        pw.println(mNetworkName);
+        pw.print("  mNetworkNameDefault=");
+        pw.println(mNetworkNameDefault);
+        pw.print("  mNetworkNameSeparator=");
+        pw.println(mNetworkNameSeparator.replace("\n","\\n"));
+        pw.print("  mPhoneSignalIconId=0x");
+        pw.print(Integer.toHexString(mPhoneSignalIconId));
+        pw.print("/");
+        pw.print("  mQSPhoneSignalIconId=0x");
+        pw.print(Integer.toHexString(mQSPhoneSignalIconId));
+        pw.print("/");
+        pw.println(getResourceName(mPhoneSignalIconId));
+        pw.print("  mDataDirectionIconId=");
+        pw.print(Integer.toHexString(mDataDirectionIconId));
+        pw.print("/");
+        pw.println(getResourceName(mDataDirectionIconId));
+        pw.print("  mDataSignalIconId=");
+        pw.print(Integer.toHexString(mDataSignalIconId));
+        pw.print("/");
+        pw.println(getResourceName(mDataSignalIconId));
+        pw.print("  mDataTypeIconId=");
+        pw.print(Integer.toHexString(mDataTypeIconId));
+        pw.print("/");
+        pw.println(getResourceName(mDataTypeIconId));
+        pw.print("  mQSDataTypeIconId=");
+        pw.print(Integer.toHexString(mQSDataTypeIconId));
+        pw.print("/");
+        pw.println(getResourceName(mQSDataTypeIconId));
+
+        pw.println("  - wifi ------");
+        pw.print("  mWifiEnabled=");
+        pw.println(mWifiEnabled);
+        pw.print("  mWifiConnected=");
+        pw.println(mWifiConnected);
+        pw.print("  mWifiRssi=");
+        pw.println(mWifiRssi);
+        pw.print("  mWifiLevel=");
+        pw.println(mWifiLevel);
+        pw.print("  mWifiSsid=");
+        pw.println(mWifiSsid);
+        pw.println(String.format("  mWifiIconId=0x%08x/%s",
+                    mWifiIconId, getResourceName(mWifiIconId)));
+        pw.println(String.format("  mQSWifiIconId=0x%08x/%s",
+                    mQSWifiIconId, getResourceName(mQSWifiIconId)));
+        pw.print("  mWifiActivity=");
+        pw.println(mWifiActivity);
+
+        if (mWimaxSupported) {
+            pw.println("  - wimax ------");
+            pw.print("  mIsWimaxEnabled="); pw.println(mIsWimaxEnabled);
+            pw.print("  mWimaxConnected="); pw.println(mWimaxConnected);
+            pw.print("  mWimaxIdle="); pw.println(mWimaxIdle);
+            pw.println(String.format("  mWimaxIconId=0x%08x/%s",
+                        mWimaxIconId, getResourceName(mWimaxIconId)));
+            pw.println(String.format("  mWimaxSignal=%d", mWimaxSignal));
+            pw.println(String.format("  mWimaxState=%d", mWimaxState));
+            pw.println(String.format("  mWimaxExtraState=%d", mWimaxExtraState));
+        }
+
+        pw.println("  - Bluetooth ----");
+        pw.print("  mBtReverseTethered=");
+        pw.println(mBluetoothTethered);
+
+        pw.println("  - connectivity ------");
+        pw.print("  mInetCondition=");
+        pw.println(mInetCondition);
+
+        pw.println("  - icons ------");
+        pw.print("  mLastPhoneSignalIconId=0x");
+        pw.print(Integer.toHexString(mLastPhoneSignalIconId));
+        pw.print("/");
+        pw.println(getResourceName(mLastPhoneSignalIconId));
+        pw.print("  mLastDataDirectionIconId=0x");
+        pw.print(Integer.toHexString(mLastDataDirectionIconId));
+        pw.print("/");
+        pw.println(getResourceName(mLastDataDirectionIconId));
+        pw.print("  mLastWifiIconId=0x");
+        pw.print(Integer.toHexString(mLastWifiIconId));
+        pw.print("/");
+        pw.println(getResourceName(mLastWifiIconId));
+        pw.print("  mLastCombinedSignalIconId=0x");
+        pw.print(Integer.toHexString(mLastCombinedSignalIconId));
+        pw.print("/");
+        pw.println(getResourceName(mLastCombinedSignalIconId));
+        pw.print("  mLastDataTypeIconId=0x");
+        pw.print(Integer.toHexString(mLastDataTypeIconId));
+        pw.print("/");
+        pw.println(getResourceName(mLastDataTypeIconId));
+        pw.print("  mLastCombinedLabel=");
+        pw.print(mLastCombinedLabel);
+        pw.println("");
+    }
+
+    private String getResourceName(int resId) {
+        if (resId != 0) {
+            final Resources res = mContext.getResources();
+            try {
+                return res.getResourceName(resId);
+            } catch (android.content.res.Resources.NotFoundException ex) {
+                return "(unknown)";
+            }
+        } else {
+            return "(null)";
+        }
+    }
+
+    private boolean mDemoMode;
+    private int mDemoInetCondition;
+    private int mDemoWifiLevel;
+    private int mDemoDataTypeIconId;
+    private int mDemoMobileLevel;
+
+    @Override
+    public void dispatchDemoCommand(String command, Bundle args) {
+        if (!mDemoMode && command.equals(COMMAND_ENTER)) {
+            mDemoMode = true;
+            mDemoWifiLevel = mWifiLevel;
+            mDemoInetCondition = mInetCondition;
+            mDemoDataTypeIconId = mDataTypeIconId;
+            mDemoMobileLevel = mLastSignalLevel;
+        } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
+            mDemoMode = false;
+            for (SignalCluster cluster : mSignalClusters) {
+                refreshSignalCluster(cluster);
+            }
+        } else if (mDemoMode && command.equals(COMMAND_NETWORK)) {
+            String airplane = args.getString("airplane");
+            if (airplane != null) {
+                boolean show = airplane.equals("show");
+                for (SignalCluster cluster : mSignalClusters) {
+                    cluster.setIsAirplaneMode(show, FLIGHT_MODE_ICON);
+                }
+            }
+            String fully = args.getString("fully");
+            if (fully != null) {
+                mDemoInetCondition = Boolean.parseBoolean(fully) ? 1 : 0;
+            }
+            String wifi = args.getString("wifi");
+            if (wifi != null) {
+                boolean show = wifi.equals("show");
+                String level = args.getString("level");
+                if (level != null) {
+                    mDemoWifiLevel = level.equals("null") ? -1
+                            : Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1);
+                }
+                int iconId = mDemoWifiLevel < 0 ? R.drawable.stat_sys_wifi_signal_null
+                        : WifiIcons.WIFI_SIGNAL_STRENGTH[mDemoInetCondition][mDemoWifiLevel];
+                for (SignalCluster cluster : mSignalClusters) {
+                    cluster.setWifiIndicators(
+                            show,
+                            iconId,
+                            mDemoInetCondition == 0,
+                            "Demo");
+                }
+            }
+            String mobile = args.getString("mobile");
+            if (mobile != null) {
+                boolean show = mobile.equals("show");
+                String datatype = args.getString("datatype");
+                if (datatype != null) {
+                    mDemoDataTypeIconId =
+                            datatype.equals("1x") ? R.drawable.stat_sys_data_fully_connected_1x :
+                            datatype.equals("3g") ? R.drawable.stat_sys_data_fully_connected_3g :
+                            datatype.equals("4g") ? R.drawable.stat_sys_data_fully_connected_4g :
+                            datatype.equals("e") ? R.drawable.stat_sys_data_fully_connected_e :
+                            datatype.equals("g") ? R.drawable.stat_sys_data_fully_connected_g :
+                            datatype.equals("h") ? R.drawable.stat_sys_data_fully_connected_h :
+                            datatype.equals("lte") ? R.drawable.stat_sys_data_fully_connected_lte :
+                            datatype.equals("roam")
+                                    ? R.drawable.stat_sys_data_fully_connected_roam :
+                            0;
+                }
+                int[][] icons = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH;
+                String level = args.getString("level");
+                if (level != null) {
+                    mDemoMobileLevel = level.equals("null") ? -1
+                            : Math.min(Integer.parseInt(level), icons[0].length - 1);
+                }
+                int iconId = mDemoMobileLevel < 0 ? R.drawable.stat_sys_signal_null :
+                        icons[mDemoInetCondition][mDemoMobileLevel];
+                for (SignalCluster cluster : mSignalClusters) {
+                    cluster.setMobileDataIndicators(
+                            show,
+                            iconId,
+                            mDemoInetCondition == 0,
+                            mDemoDataTypeIconId,
+                            "Demo",
+                            "Demo");
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
index 98d205a..1eb678d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,65 +16,15 @@
 
 package com.android.systemui.statusbar.policy;
 
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.UserHandle;
-
-import com.android.internal.view.RotationPolicy;
-
-import java.util.concurrent.CopyOnWriteArrayList;
-
-public final class RotationLockController {
-    private final Context mContext;
-    private final CopyOnWriteArrayList<RotationLockControllerCallback> mCallbacks =
-            new CopyOnWriteArrayList<RotationLockControllerCallback>();
-
-    private final RotationPolicy.RotationPolicyListener mRotationPolicyListener =
-            new RotationPolicy.RotationPolicyListener() {
-        @Override
-        public void onChange() {
-            notifyChanged();
-        }
-    };
+public interface RotationLockController extends Disposable {
+    int getRotationLockOrientation();
+    boolean isRotationLockAffordanceVisible();
+    boolean isRotationLocked();
+    void setRotationLocked(boolean locked);
+    void addRotationLockControllerCallback(RotationLockControllerCallback callback);
+    void removeRotationLockControllerCallback(RotationLockControllerCallback callback);
 
     public interface RotationLockControllerCallback {
-        public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible);
-    }
-
-    public RotationLockController(Context context) {
-        mContext = context;
-        RotationPolicy.registerRotationPolicyListener(mContext,
-                mRotationPolicyListener, UserHandle.USER_ALL);
-    }
-
-    public void addRotationLockControllerCallback(RotationLockControllerCallback callback) {
-        mCallbacks.add(callback);
-    }
-
-    public int getRotationLockOrientation() {
-        return RotationPolicy.getRotationLockOrientation(mContext);
-    }
-
-    public boolean isRotationLocked() {
-        return RotationPolicy.isRotationLocked(mContext);
-    }
-
-    public void setRotationLocked(boolean locked) {
-        RotationPolicy.setRotationLock(mContext, locked);
-    }
-
-    public boolean isRotationLockAffordanceVisible() {
-        return RotationPolicy.isRotationLockToggleVisible(mContext);
-    }
-
-    public void release() {
-        RotationPolicy.unregisterRotationPolicyListener(mContext, mRotationPolicyListener);
-    }
-
-    private void notifyChanged() {
-        for (RotationLockControllerCallback callback : mCallbacks) {
-            callback.onRotationLockStateChanged(RotationPolicy.isRotationLocked(mContext),
-                    RotationPolicy.isRotationLockToggleVisible(mContext));
-        }
+        void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
new file mode 100644
index 0000000..caa07ef
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.content.Context;
+import android.os.UserHandle;
+
+import com.android.internal.view.RotationPolicy;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/** Platform implementation of the rotation lock controller. **/
+public final class RotationLockControllerImpl implements RotationLockController {
+    private final Context mContext;
+    private final CopyOnWriteArrayList<RotationLockControllerCallback> mCallbacks =
+            new CopyOnWriteArrayList<RotationLockControllerCallback>();
+
+    private final RotationPolicy.RotationPolicyListener mRotationPolicyListener =
+            new RotationPolicy.RotationPolicyListener() {
+        @Override
+        public void onChange() {
+            notifyChanged();
+        }
+    };
+
+    public RotationLockControllerImpl(Context context) {
+        mContext = context;
+        RotationPolicy.registerRotationPolicyListener(mContext,
+                mRotationPolicyListener, UserHandle.USER_ALL);
+    }
+
+    public void addRotationLockControllerCallback(RotationLockControllerCallback callback) {
+        mCallbacks.add(callback);
+    }
+
+    public void removeRotationLockControllerCallback(RotationLockControllerCallback callback) {
+        mCallbacks.remove(callback);
+    }
+
+    public int getRotationLockOrientation() {
+        return RotationPolicy.getRotationLockOrientation(mContext);
+    }
+
+    public boolean isRotationLocked() {
+        return RotationPolicy.isRotationLocked(mContext);
+    }
+
+    public void setRotationLocked(boolean locked) {
+        RotationPolicy.setRotationLock(mContext, locked);
+    }
+
+    public boolean isRotationLockAffordanceVisible() {
+        return RotationPolicy.isRotationLockToggleVisible(mContext);
+    }
+
+    @Override
+    public void dispose() {
+        RotationPolicy.unregisterRotationPolicyListener(mContext, mRotationPolicyListener);
+    }
+
+    private void notifyChanged() {
+        for (RotationLockControllerCallback callback : mCallbacks) {
+            callback.onRotationLockStateChanged(RotationPolicy.isRotationLocked(mContext),
+                    RotationPolicy.isRotationLockToggleVisible(mContext));
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TetheringController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TetheringController.java
new file mode 100644
index 0000000..143ebaa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TetheringController.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+public interface TetheringController {
+    void addCallback(Callback callback);
+    void removeCallback(Callback callback);
+    boolean isHotspotEnabled();
+    boolean isHotspotSupported();
+
+    public interface Callback {
+        void onHotspotChanged(boolean hotspot);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
new file mode 100644
index 0000000..6225c12
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.service.notification.Condition;
+
+public interface ZenModeController {
+    void addCallback(Callback callback);
+    void removeCallback(Callback callback);
+    void setZen(boolean zen);
+    boolean isZen();
+    void requestConditions(boolean request);
+    void select(Condition condition);
+
+    public static class Callback {
+        public void onZenChanged(boolean zen) {}
+        public void onConditionsChanged(Condition[] conditions) {}
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
new file mode 100644
index 0000000..d760f78
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.app.INotificationManager;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings.Global;
+import android.service.notification.Condition;
+import android.service.notification.IConditionListener;
+import android.util.Slog;
+
+import com.android.systemui.qs.GlobalSetting;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+
+/** Platform implementation of the zen mode controller. **/
+public class ZenModeControllerImpl implements ZenModeController {
+    private static final String TAG = "ZenModeControllerImpl";
+
+    private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
+    private final Context mContext;
+    private final GlobalSetting mSetting;
+    private final INotificationManager mNoMan;
+    private final LinkedHashMap<Uri, Condition> mConditions = new LinkedHashMap<Uri, Condition>();
+
+    private boolean mRequesting;
+
+    public ZenModeControllerImpl(Context context, Handler handler) {
+        mContext = context;
+        mSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE) {
+            @Override
+            protected void handleValueChanged(int value) {
+                fireZenChanged(value != 0);
+            }
+        };
+        mNoMan = INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+    }
+
+    @Override
+    public void addCallback(Callback callback) {
+        mCallbacks.add(callback);
+    }
+
+    @Override
+    public void removeCallback(Callback callback) {
+        mCallbacks.remove(callback);
+    }
+
+    @Override
+    public boolean isZen() {
+        return mSetting.getValue() != 0;
+    }
+
+    @Override
+    public void setZen(boolean zen) {
+        mSetting.setValue(zen ? 1 : 0);
+    }
+
+    @Override
+    public void requestConditions(boolean request) {
+        mRequesting = request;
+        try {
+            mNoMan.requestZenModeConditions(mListener, request ? Condition.FLAG_RELEVANT_NOW : 0);
+        } catch (RemoteException e) {
+            // noop
+        }
+        if (!mRequesting) {
+            mConditions.clear();
+        }
+    }
+
+    @Override
+    public void select(Condition condition) {
+        try {
+            mNoMan.setZenModeCondition(condition == null ? null : condition.id);
+        } catch (RemoteException e) {
+            // noop
+        }
+    }
+
+    private void fireZenChanged(boolean zen) {
+        for (Callback cb : mCallbacks) {
+            cb.onZenChanged(zen);
+        }
+    }
+
+    private void fireConditionsChanged(Condition[] conditions) {
+        for (Callback cb : mCallbacks) {
+            cb.onConditionsChanged(conditions);
+        }
+    }
+
+    private void updateConditions(Condition[] conditions) {
+        if (conditions == null || conditions.length == 0) return;
+        for (Condition c : conditions) {
+            if ((c.flags & Condition.FLAG_RELEVANT_NOW) == 0) continue;
+            mConditions.put(c.id, c);
+        }
+        fireConditionsChanged(
+                mConditions.values().toArray(new Condition[mConditions.values().size()]));
+    }
+
+    private final IConditionListener mListener = new IConditionListener.Stub() {
+        @Override
+        public void onConditionsReceived(Condition[] conditions) {
+            Slog.d(TAG, "onConditionsReceived " + (conditions == null ? 0 : conditions.length)
+                    + " mRequesting=" + mRequesting); 
+            if (!mRequesting) return;
+            updateConditions(conditions);
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index 4121a40..deab757 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -28,6 +28,8 @@
     private int mScrollY;
     private boolean mDimmed;
     private View mActivatedChild;
+    private float mOverScrollTopAmount;
+    private float mOverScrollBottomAmount;
 
     public int getScrollY() {
         return mScrollY;
@@ -72,4 +74,16 @@
     public View getActivatedChild() {
         return mActivatedChild;
     }
+
+    public void setOverScrollAmount(float amount, boolean onTop) {
+        if (onTop) {
+            mOverScrollTopAmount = amount;
+        } else {
+            mOverScrollBottomAmount = amount;
+        }
+    }
+
+    public float getOverScrollAmount(boolean top) {
+        return top ? mOverScrollTopAmount : mOverScrollBottomAmount;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 5849afb..fbb6695 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -53,6 +53,7 @@
 
     private static final String TAG = "NotificationStackScrollLayout";
     private static final boolean DEBUG = false;
+    private static final float RUBBER_BAND_FACTOR = 0.35f;
 
     /**
      * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
@@ -70,8 +71,8 @@
     private int mTouchSlop;
     private int mMinimumVelocity;
     private int mMaximumVelocity;
-    private int mOverscrollDistance;
     private int mOverflingDistance;
+    private float mMaxOverScroll;
     private boolean mIsBeingDragged;
     private int mLastMotionY;
     private int mActivePointerId;
@@ -107,6 +108,16 @@
     private ArrayList<View> mSwipedOutViews = new ArrayList<View>();
     private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
 
+    /**
+     * The raw amount of the overScroll on the top, which is not rubber-banded.
+     */
+    private float mOverScrolledTopPixels;
+
+    /**
+     * The raw amount of the overScroll on the bottom, which is not rubber-banded.
+     */
+    private float mOverScrolledBottomPixels;
+
     private OnChildLocationsChangedListener mListener;
     private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
     private boolean mNeedsAnimation;
@@ -175,7 +186,6 @@
         mTouchSlop = configuration.getScaledTouchSlop();
         mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
-        mOverscrollDistance = configuration.getScaledOverscrollDistance();
         mOverflingDistance = configuration.getScaledOverflingDistance();
         float densityScale = getResources().getDisplayMetrics().density;
         float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
@@ -544,7 +554,7 @@
                  * will be false if being flinged.
                  */
                 if (!mScroller.isFinished()) {
-                    mScroller.abortAnimation();
+                    mScroller.forceFinished(true);
                 }
 
                 // Remember where the motion event started
@@ -572,40 +582,23 @@
                 if (mIsBeingDragged) {
                     // Scroll to follow the motion event
                     mLastMotionY = y;
-
-                    final int oldX = mScrollX;
-                    final int oldY = mOwnScrollY;
                     final int range = getScrollRange();
-                    final int overscrollMode = getOverScrollMode();
-                    final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
-                            (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
+
+                    float scrollAmount;
+                    if (deltaY < 0) {
+                        scrollAmount = overScrollDown(deltaY);
+                    } else {
+                        scrollAmount = overScrollUp(deltaY, range);
+                    }
 
                     // Calling overScrollBy will call onOverScrolled, which
                     // calls onScrollChanged if applicable.
-                    if (overScrollBy(0, deltaY, 0, mOwnScrollY,
-                            0, range, 0, mOverscrollDistance, true)) {
-                        // Break our velocity if we hit a scroll barrier.
-                        mVelocityTracker.clear();
+                    if (scrollAmount != 0.0f) {
+                        // The scrolling motion could not be compensated with the
+                        // existing overScroll, we have to scroll the view
+                        overScrollBy(0, (int) scrollAmount, 0, mOwnScrollY,
+                                0, range, 0, getHeight() / 2, true);
                     }
-                    // TODO: Overscroll
-//                    if (canOverscroll) {
-//                        final int pulledToY = oldY + deltaY;
-//                        if (pulledToY < 0) {
-//                            mEdgeGlowTop.onPull((float) deltaY / getHeight());
-//                            if (!mEdgeGlowBottom.isFinished()) {
-//                                mEdgeGlowBottom.onRelease();
-//                            }
-//                        } else if (pulledToY > range) {
-//                            mEdgeGlowBottom.onPull((float) deltaY / getHeight());
-//                            if (!mEdgeGlowTop.isFinished()) {
-//                                mEdgeGlowTop.onRelease();
-//                            }
-//                        }
-//                        if (mEdgeGlowTop != null
-//                                && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())){
-//                            postInvalidateOnAnimation();
-//                        }
-//                    }
                 }
                 break;
             case MotionEvent.ACTION_UP:
@@ -652,6 +645,68 @@
         return true;
     }
 
+    /**
+     * Perform a scroll upwards and adapt the overscroll amounts accordingly
+     *
+     * @param deltaY The amount to scroll upwards, has to be positive.
+     * @return The amount of scrolling to be performed by the scroller,
+     *         not handled by the overScroll amount.
+     */
+    private float overScrollUp(int deltaY, int range) {
+        deltaY = Math.max(deltaY, 0);
+        float currentTopAmount = getCurrentOverScrollAmount(true);
+        float newTopAmount = currentTopAmount - deltaY;
+        if (currentTopAmount > 0) {
+            setOverScrollAmount(newTopAmount, true /* onTop */,
+                    false /* animate */);
+        }
+        // Top overScroll might not grab all scrolling motion,
+        // we have to scroll as well.
+        float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
+        float newScrollY = mOwnScrollY + scrollAmount;
+        if (newScrollY > range) {
+            float currentBottomPixels = getCurrentOverScrolledPixels(false);
+            // We overScroll on the top
+            setOverScrolledPixels(currentBottomPixels + newScrollY - range,
+                    false /* onTop */,
+                    false /* animate */);
+            mOwnScrollY = range;
+            scrollAmount = 0.0f;
+        }
+        return scrollAmount;
+    }
+
+    /**
+     * Perform a scroll downward and adapt the overscroll amounts accordingly
+     *
+     * @param deltaY The amount to scroll downwards, has to be negative.
+     * @return The amount of scrolling to be performed by the scroller,
+     *         not handled by the overScroll amount.
+     */
+    private float overScrollDown(int deltaY) {
+        deltaY = Math.min(deltaY, 0);
+        float currentBottomAmount = getCurrentOverScrollAmount(false);
+        float newBottomAmount = currentBottomAmount + deltaY;
+        if (currentBottomAmount > 0) {
+            setOverScrollAmount(newBottomAmount, false /* onTop */,
+                    false /* animate */);
+        }
+        // Bottom overScroll might not grab all scrolling motion,
+        // we have to scroll as well.
+        float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
+        float newScrollY = mOwnScrollY + scrollAmount;
+        if (newScrollY < 0) {
+            float currentTopPixels = getCurrentOverScrolledPixels(true);
+            // We overScroll on the top
+            setOverScrolledPixels(currentTopPixels - newScrollY,
+                    true /* onTop */,
+                    false /* animate */);
+            mOwnScrollY = 0;
+            scrollAmount = 0.0f;
+        }
+        return scrollAmount;
+    }
+
     private void onSecondaryPointerUp(MotionEvent ev) {
         final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
                 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
@@ -701,23 +756,16 @@
 
             if (oldX != x || oldY != y) {
                 final int range = getScrollRange();
-                final int overscrollMode = getOverScrollMode();
-                final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
-                        (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
+                if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
+                    float currVelocity = mScroller.getCurrVelocity();
+                    if (currVelocity >= mMinimumVelocity) {
+                        mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
+                    }
+                }
 
                 overScrollBy(x - oldX, y - oldY, oldX, oldY, 0, range,
-                        0, mOverflingDistance, false);
+                        0, (int) (mMaxOverScroll), false);
                 onScrollChanged(mScrollX, mOwnScrollY, oldX, oldY);
-
-                if (canOverscroll) {
-                    // TODO: Overscroll
-//                    if (y < 0 && oldY >= 0) {
-//                        mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());
-//                    } else if (y > range && oldY <= range) {
-//                        mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());
-//                    }
-                }
-                updateChildren();
             }
 
             // Keep on drawing until the animation has finished.
@@ -725,6 +773,81 @@
         }
     }
 
+    @Override
+    protected int computeVerticalScrollRange() {
+        // needed for the overScroller
+        return mContentHeight;
+    }
+
+    /**
+     * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
+     * overscroll effect based on numPixels. By default this will also cancel animations on the
+     * same overScroll edge.
+     *
+     * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
+     *                  the rubber-banding logic.
+     * @param onTop Should the effect be applied on top of the scroller.
+     * @param animate Should an animation be performed.
+     */
+    public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
+        setOverScrollAmount(numPixels * RUBBER_BAND_FACTOR, onTop, animate, true);
+    }
+
+    /**
+     * Set the effective overScroll amount which will be directly reflected in the layout.
+     * By default this will also cancel animations on the same overScroll edge.
+     *
+     * @param amount The amount to overScroll by.
+     * @param onTop Should the effect be applied on top of the scroller.
+     * @param animate Should an animation be performed.
+     */
+    public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
+        setOverScrollAmount(amount, onTop, animate, true);
+    }
+
+    /**
+     * Set the effective overScroll amount which will be directly reflected in the layout.
+     *
+     * @param amount The amount to overScroll by.
+     * @param onTop Should the effect be applied on top of the scroller.
+     * @param animate Should an animation be performed.
+     * @param cancelAnimators Should running animations be cancelled.
+     */
+    public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
+            boolean cancelAnimators) {
+        if (cancelAnimators) {
+            mStateAnimator.cancelOverScrollAnimators(onTop);
+        }
+        setOverScrollAmountInternal(amount, onTop, animate);
+    }
+
+    private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate) {
+        amount = Math.max(0, amount);
+        if (animate) {
+            mStateAnimator.animateOverScrollToAmount(amount, onTop);
+        } else {
+            setOverScrolledPixels(amount / RUBBER_BAND_FACTOR, onTop);
+            mAmbientState.setOverScrollAmount(amount, onTop);
+            requestChildrenUpdate();
+        }
+    }
+
+    public float getCurrentOverScrollAmount(boolean top) {
+        return mAmbientState.getOverScrollAmount(top);
+    }
+
+    public float getCurrentOverScrolledPixels(boolean top) {
+        return top? mOverScrolledTopPixels : mOverScrolledBottomPixels;
+    }
+
+    private void setOverScrolledPixels(float amount, boolean onTop) {
+        if (onTop) {
+            mOverScrolledTopPixels = amount;
+        } else {
+            mOverScrolledBottomPixels = amount;
+        }
+    }
+
     private void customScrollTo(int y) {
         mOwnScrollY = y;
         updateChildren();
@@ -738,18 +861,41 @@
             final int oldY = mOwnScrollY;
             mScrollX = scrollX;
             mOwnScrollY = scrollY;
-            invalidateParentIfNeeded();
-            onScrollChanged(mScrollX, mOwnScrollY, oldX, oldY);
             if (clampedY) {
-                mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange());
+                springBack();
+            } else {
+                onScrollChanged(mScrollX, mOwnScrollY, oldX, oldY);
+                invalidateParentIfNeeded();
+                updateChildren();
             }
-            updateChildren();
         } else {
             customScrollTo(scrollY);
             scrollTo(scrollX, mScrollY);
         }
     }
 
+    private void springBack() {
+        int scrollRange = getScrollRange();
+        boolean overScrolledTop = mOwnScrollY <= 0;
+        boolean overScrolledBottom = mOwnScrollY >= scrollRange;
+        if (overScrolledTop || overScrolledBottom) {
+            boolean onTop;
+            float newAmount;
+            if (overScrolledTop) {
+                onTop = true;
+                newAmount = -mOwnScrollY;
+                mOwnScrollY = 0;
+            } else {
+                onTop = false;
+                newAmount = mOwnScrollY - scrollRange;
+                mOwnScrollY = scrollRange;
+            }
+            setOverScrollAmount(newAmount, onTop, false);
+            setOverScrollAmount(0.0f, onTop, true);
+            mScroller.forceFinished(true);
+        }
+    }
+
     private int getScrollRange() {
         int scrollRange = 0;
         ExpandableView firstChild = (ExpandableView) getFirstChildNotGone();
@@ -841,7 +987,23 @@
             int height = (int) getLayoutHeight();
             int bottom = getContentHeight();
 
-            mScroller.fling(mScrollX, mOwnScrollY, 0, velocityY, 0, 0, 0,
+            float topAmount = getCurrentOverScrollAmount(true);
+            float bottomAmount = getCurrentOverScrollAmount(false);
+            if (velocityY < 0 && topAmount > 0) {
+                mOwnScrollY -= (int) topAmount;
+                setOverScrollAmount(0, true, false);
+                mMaxOverScroll = Math.abs(velocityY) / 1000f * RUBBER_BAND_FACTOR
+                        * mOverflingDistance + topAmount;
+            } else if (velocityY > 0 && bottomAmount > 0) {
+                mOwnScrollY += bottomAmount;
+                setOverScrollAmount(0, false, false);
+                mMaxOverScroll = Math.abs(velocityY) / 1000f * RUBBER_BAND_FACTOR
+                        * mOverflingDistance + bottomAmount;
+            } else {
+                // it will be set once we reach the boundary
+                mMaxOverScroll = 0.0f;
+            }
+            mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0,
                     Math.max(0, bottom - height), 0, height/2);
 
             postInvalidateOnAnimation();
@@ -853,11 +1015,12 @@
 
         recycleVelocityTracker();
 
-        // TODO: Overscroll
-//        if (mEdgeGlowTop != null) {
-//            mEdgeGlowTop.onRelease();
-//            mEdgeGlowBottom.onRelease();
-//        }
+        if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
+            setOverScrollAmount(0, true /* onTop */, true /* animate */);
+        }
+        if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
+            setOverScrollAmount(0, false /* onTop */, true /* animate */);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index bd9de82..d572ea5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -128,7 +128,10 @@
         algorithmState.scrolledPixelsTop = 0;
         algorithmState.itemsInBottomStack = 0.0f;
         algorithmState.partialInBottom = 0.0f;
-        algorithmState.scrollY = ambientState.getScrollY() + mCollapsedSize;
+        float topOverScroll = ambientState.getOverScrollAmount(true /* onTop */);
+        float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
+        algorithmState.scrollY = (int) (ambientState.getScrollY() + mCollapsedSize
+                + bottomOverScroll - topOverScroll);
 
         updateVisibleChildren(resultState, algorithmState);
 
@@ -215,7 +218,6 @@
      *
      * @param resultState The result state to update if a change to the properties of a child occurs
      * @param algorithmState The state in which the current pass of the algorithm is currently in
-     *                       and which will be updated
      */
     private void updatePositionsForState(StackScrollState resultState,
             StackScrollAlgorithmState algorithmState) {
@@ -295,7 +297,7 @@
             // The first card is always rendered.
             if (i == 0) {
                 childViewState.alpha = 1.0f;
-                childViewState.yTranslation = 0;
+                childViewState.yTranslation = Math.max(mCollapsedSize - algorithmState.scrollY, 0);
                 childViewState.location = StackScrollState.ViewState.LOCATION_FIRST_CARD;
             }
             if (childViewState.location == StackScrollState.ViewState.LOCATION_UNKNOWN) {
@@ -454,7 +456,6 @@
      *
      * @param resultState The result state to update if a height change of an child occurs
      * @param algorithmState The state in which the current pass of the algorithm is currently in
-     *                       and which will be updated
      */
     private void findNumberOfItemsInTopStackAndUpdateState(StackScrollState resultState,
             StackScrollAlgorithmState algorithmState) {
@@ -472,7 +473,7 @@
                     + childHeight
                     + mPaddingBetweenElements;
             if (yPositionInScrollView < algorithmState.scrollY) {
-                if (i == 0 && algorithmState.scrollY == mCollapsedSize) {
+                if (i == 0 && algorithmState.scrollY <= mCollapsedSize) {
 
                     // The starting position of the bottom stack peek
                     int bottomPeekStart = mInnerHeight - mBottomStackPeekSize;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index ca383aa..5ac51f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -18,7 +18,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
@@ -73,6 +72,9 @@
     private AnimationFilter mAnimationFilter = new AnimationFilter();
     private long mCurrentLength;
 
+    private ValueAnimator mTopOverScrollAnimator;
+    private ValueAnimator mBottomOverScrollAnimator;
+
     public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
         mHostLayout = hostLayout;
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(),
@@ -510,7 +512,7 @@
             ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
             StackScrollState finalState) {
         mNewEvents.clear();
-        for (NotificationStackScrollLayout.AnimationEvent event: animationEvents) {
+        for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
             View changingView = event.changingView;
             if (!mHandledEvents.contains(event)) {
                 if (event.animationType == NotificationStackScrollLayout.AnimationEvent
@@ -532,4 +534,34 @@
             }
         }
     }
+
+    public void animateOverScrollToAmount(float targetAmount, final boolean onTop) {
+        final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
+        cancelOverScrollAnimators(onTop);
+        ValueAnimator overScrollAnimator = ValueAnimator.ofFloat(startOverScrollAmount,
+                targetAmount);
+        overScrollAnimator.setDuration(ANIMATION_DURATION_STANDARD);
+        overScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                float currentOverScroll = (float) animation.getAnimatedValue();
+                mHostLayout.setOverScrollAmount(currentOverScroll, onTop, false /* animate */,
+                        false /* cancelAnimators */);
+            }
+        });
+        overScrollAnimator.setInterpolator(mFastOutSlowInInterpolator);
+        overScrollAnimator.start();
+        if (onTop) {
+            mTopOverScrollAnimator = overScrollAnimator;
+        } else {
+            mBottomOverScrollAnimator = overScrollAnimator;
+        }
+    }
+
+    public void cancelOverScrollAnimators(boolean onTop) {
+        ValueAnimator currentAnimator = onTop ? mTopOverScrollAnimator : mBottomOverScrollAnimator;
+        if (currentAnimator != null) {
+            currentAnimator.cancel();
+        }
+    }
 }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index b1fea03..6341a0c 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -44,6 +44,7 @@
 import android.app.KeyguardManager;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -301,6 +302,11 @@
             throw new AndroidRuntimeException(
                     "You cannot combine swipe dismissal and the action bar.");
         }
+
+        if (featureId == FEATURE_INDETERMINATE_PROGRESS &&
+                getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+            throw new AndroidRuntimeException("You cannot use indeterminate progress on a watch.");
+        }
         return super.requestFeature(featureId);
     }
 
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c1ffd56..741bf95 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -29,6 +29,7 @@
 import static android.net.ConnectivityManager.TYPE_PROXY;
 import static android.net.ConnectivityManager.getNetworkTypeName;
 import static android.net.ConnectivityManager.isNetworkTypeValid;
+import static android.net.ConnectivityServiceProtocol.NetworkFactoryProtocol;
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 
@@ -66,10 +67,12 @@
 import android.net.LinkQualityInfo;
 import android.net.MobileDataStateTracker;
 import android.net.Network;
+import android.net.NetworkCapabilities;
 import android.net.NetworkConfig;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkQuotaInfo;
+import android.net.NetworkRequest;
 import android.net.NetworkState;
 import android.net.NetworkStateTracker;
 import android.net.NetworkUtils;
@@ -119,6 +122,7 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.XmlUtils;
 import com.android.server.am.BatteryStatsService;
@@ -372,6 +376,12 @@
      */
     private static final int EVENT_PROXY_HAS_CHANGED = 16;
 
+    /**
+     * used internally when registering NetworkFactories
+     * obj = Messenger
+     */
+    private static final int EVENT_REGISTER_NETWORK_FACTORY = 17;
+
     /** Handler used for internal events. */
     private InternalHandler mHandler;
     /** Handler used for incoming {@link NetworkStateTracker} events. */
@@ -461,6 +471,12 @@
             NetworkFactory netFactory) {
         if (DBG) log("ConnectivityService starting up");
 
+        NetworkCapabilities netCap = new NetworkCapabilities();
+        netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+        netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+        NetworkRequest netRequest = new NetworkRequest(netCap);
+        mNetworkRequests.append(netRequest.requestId, netRequest);
+
         HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
         handlerThread.start();
         mHandler = new InternalHandler(handlerThread.getLooper());
@@ -2084,7 +2100,7 @@
                     getConnectivityChangeDelay());
         }
         try {
-            mNetd.removeNetwork(thisNetId);
+//            mNetd.removeNetwork(thisNetId);
         } catch (Exception e) {
             loge("Exception removing network: " + e);
         } finally {
@@ -2388,7 +2404,7 @@
             int thisNetId = nextNetId();
             thisNet.setNetId(thisNetId);
             try {
-                mNetd.createNetwork(thisNetId, thisIface);
+//                mNetd.createNetwork(thisNetId, thisIface);
             } catch (Exception e) {
                 loge("Exception creating network :" + e);
                 teardown(thisNet);
@@ -2420,7 +2436,7 @@
             int thisNetId = nextNetId();
             thisNet.setNetId(thisNetId);
             try {
-                mNetd.createNetwork(thisNetId, thisIface);
+//                mNetd.createNetwork(thisNetId, thisIface);
             } catch (Exception e) {
                 loge("Exception creating network :" + e);
                 teardown(thisNet);
@@ -3048,6 +3064,22 @@
         public void handleMessage(Message msg) {
             NetworkInfo info;
             switch (msg.what) {
+                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
+                    AsyncChannel ac = (AsyncChannel) msg.obj;
+                    if (mNetworkFactories.contains(ac)) {
+                        if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+                            if (VDBG) log("NetworkFactory connected");
+                            for (int i = 0; i < mNetworkRequests.size(); i++) {
+                                ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK,
+                                    mNetworkRequests.valueAt(i));
+                            }
+                        } else {
+                            loge("Error connecting NetworkFactory");
+                            mNetworkFactories.remove((AsyncChannel) msg.obj);
+                        }
+                    }
+                    break;
+                }
                 case NetworkStateTracker.EVENT_STATE_CHANGED: {
                     info = (NetworkInfo) msg.obj;
                     NetworkInfo.State state = info.getState();
@@ -3241,6 +3273,10 @@
                     handleApplyDefaultProxy((ProxyInfo)msg.obj);
                     break;
                 }
+                case EVENT_REGISTER_NETWORK_FACTORY: {
+                    handleRegisterNetworkFactory((Messenger)msg.obj);
+                    break;
+                }
             }
         }
     }
@@ -5081,4 +5117,21 @@
         long wakeupTime = SystemClock.elapsedRealtime() + timeoutInMilliseconds;
         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, wakeupTime, intent);
     }
+
+    private final ArrayList<AsyncChannel> mNetworkFactories = new ArrayList<AsyncChannel>();
+
+    public void registerNetworkFactory(Messenger messenger) {
+        enforceConnectivityInternalPermission();
+
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, messenger));
+    }
+
+    private void handleRegisterNetworkFactory(Messenger messenger) {
+        if (VDBG) log("Got NetworkFactory Messenger");
+        AsyncChannel ac = new AsyncChannel();
+        mNetworkFactories.add(ac);
+        ac.connect(mContext, mTrackerHandler, messenger);
+    }
+
+    private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<NetworkRequest>();
 }
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 10315a7..0b9570d 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -1464,6 +1464,7 @@
 
     private boolean needsToShowImeSwitchOngoingNotification() {
         if (!mShowOngoingImeSwitcherForPhones) return false;
+        if (mSwitchingDialog != null) return false;
         if (isScreenLocked()) return false;
         synchronized (mMethodMap) {
             List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
@@ -2812,6 +2813,7 @@
             mSwitchingDialog.getWindow().getAttributes().privateFlags |=
                     WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
             mSwitchingDialog.getWindow().getAttributes().setTitle("Select input method");
+            updateImeWindowStatusLocked();
             mSwitchingDialog.show();
         }
     }
@@ -2869,6 +2871,7 @@
             mSwitchingDialog = null;
         }
 
+        updateImeWindowStatusLocked();
         mDialogBuilder = null;
         mIms = null;
     }
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index f587ccc..b2aaf74 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -34,6 +34,7 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentValues;
@@ -857,7 +858,7 @@
         checkManageAccountsPermission();
         UserHandle user = Binder.getCallingUserHandle();
         UserAccounts accounts = getUserAccountsForCaller();
-        if (!canUserModifyAccounts(Binder.getCallingUid())) {
+        if (!canUserModifyAccounts(Binder.getCallingUid(), account.type)) {
             try {
                 response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
                         "User cannot modify accounts");
@@ -1512,7 +1513,7 @@
         checkManageAccountsPermission();
 
         // Is user disallowed from modifying accounts?
-        if (!canUserModifyAccounts(Binder.getCallingUid())) {
+        if (!canUserModifyAccounts(Binder.getCallingUid(), accountType)) {
             try {
                 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
                         "User is not allowed to add an account!");
@@ -2758,7 +2759,7 @@
                 Manifest.permission.USE_CREDENTIALS);
     }
 
-    private boolean canUserModifyAccounts(int callingUid) {
+    private boolean canUserModifyAccounts(int callingUid, String accountType) {
         if (callingUid != Process.myUid()) {
             if (getUserManager().getUserRestrictions(
                     new UserHandle(UserHandle.getUserId(callingUid)))
@@ -2766,6 +2767,15 @@
                 return false;
             }
         }
+
+        DevicePolicyManager dpm = (DevicePolicyManager) mContext
+                .getSystemService(Context.DEVICE_POLICY_SERVICE);
+        String[] typesArray = dpm.getAccountTypesWithManagementDisabled();
+        for (String forbiddenType : typesArray) {
+            if (forbiddenType.equals(accountType)) {
+                return false;
+            }
+        }
         return true;
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5358c1a..e7e2f4d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -722,6 +722,8 @@
     private static final String TAG_URI_GRANTS = "uri-grants";
     private static final String TAG_URI_GRANT = "uri-grant";
     private static final String ATTR_USER_HANDLE = "userHandle";
+    private static final String ATTR_SOURCE_USER_ID = "sourceUserId";
+    private static final String ATTR_TARGET_USER_ID = "targetUserId";
     private static final String ATTR_SOURCE_PKG = "sourcePkg";
     private static final String ATTR_TARGET_PKG = "targetPkg";
     private static final String ATTR_URI = "uri";
@@ -739,10 +741,12 @@
             mGrantedUriPermissions = new SparseArray<ArrayMap<GrantUri, UriPermission>>();
 
     public static class GrantUri {
+        public final int sourceUserId;
         public final Uri uri;
-        public final boolean prefix;
+        public boolean prefix;
 
-        public GrantUri(Uri uri, boolean prefix) {
+        public GrantUri(int sourceUserId, Uri uri, boolean prefix) {
+            this.sourceUserId = sourceUserId;
             this.uri = uri;
             this.prefix = prefix;
         }
@@ -756,18 +760,28 @@
         public boolean equals(Object o) {
             if (o instanceof GrantUri) {
                 GrantUri other = (GrantUri) o;
-                return uri.equals(other.uri) && prefix == other.prefix;
+                return uri.equals(other.uri) && (sourceUserId == other.sourceUserId)
+                        && prefix == other.prefix;
             }
             return false;
         }
 
         @Override
         public String toString() {
-            if (prefix) {
-                return uri.toString() + " [prefix]";
-            } else {
-                return uri.toString();
-            }
+            String result = Integer.toString(sourceUserId) + " @ " + uri.toString();
+            if (prefix) result += " [prefix]";
+            return result;
+        }
+
+        public String toSafeString() {
+            String result = Integer.toString(sourceUserId) + " @ " + uri.toSafeString();
+            if (prefix) result += " [prefix]";
+            return result;
+        }
+
+        public static GrantUri resolve(int defaultSourceUserHandle, Uri uri) {
+            return new GrantUri(ContentProvider.getUserIdFromUri(uri, defaultSourceUserHandle),
+                    ContentProvider.getUriWithoutUserId(uri), false);
         }
     }
 
@@ -1328,14 +1342,12 @@
                 String host = "";
                 String port = "";
                 String exclList = "";
-                String pacFileUrl = "";
+                Uri pacFileUrl = Uri.EMPTY;
                 if (proxy != null) {
                     host = proxy.getHost();
                     port = Integer.toString(proxy.getPort());
                     exclList = proxy.getExclusionListAsString();
-                    if (proxy.getPacFileUrl() != null) {
-                        pacFileUrl = proxy.getPacFileUrl().toString();
-                    }
+                    pacFileUrl = proxy.getPacFileUrl();
                 }
                 synchronized (ActivityManagerService.this) {
                     for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
@@ -5993,9 +6005,9 @@
      * in {@link ContentProvider}.
      */
     private final boolean checkHoldingPermissionsLocked(
-            IPackageManager pm, ProviderInfo pi, Uri uri, int uid, final int modeFlags) {
+            IPackageManager pm, ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) {
         if (DEBUG_URI_PERMISSION) Slog.v(TAG,
-                "checkHoldingPermissionsLocked: uri=" + uri + " uid=" + uid);
+                "checkHoldingPermissionsLocked: uri=" + grantUri + " uid=" + uid);
 
         if (pi.applicationInfo.uid == uid) {
             return true;
@@ -6024,7 +6036,7 @@
             // check if target holds any <path-permission> that match uri
             final PathPermission[] pps = pi.pathPermissions;
             if (pps != null) {
-                final String path = uri.getPath();
+                final String path = grantUri.uri.getPath();
                 int i = pps.length;
                 while (i > 0 && (!readMet || !writeMet)) {
                     i--;
@@ -6089,32 +6101,33 @@
         return pi;
     }
 
-    private UriPermission findUriPermissionLocked(int targetUid, GrantUri uri) {
+    private UriPermission findUriPermissionLocked(int targetUid, GrantUri grantUri) {
         final ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
         if (targetUris != null) {
-            return targetUris.get(uri);
+            return targetUris.get(grantUri);
         }
         return null;
     }
 
     private UriPermission findOrCreateUriPermissionLocked(String sourcePkg,
-            String targetPkg, int targetUid, GrantUri uri) {
+            String targetPkg, int targetUid, GrantUri grantUri) {
         ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
         if (targetUris == null) {
             targetUris = Maps.newArrayMap();
             mGrantedUriPermissions.put(targetUid, targetUris);
         }
 
-        UriPermission perm = targetUris.get(uri);
+        UriPermission perm = targetUris.get(grantUri);
         if (perm == null) {
-            perm = new UriPermission(sourcePkg, targetPkg, targetUid, uri);
-            targetUris.put(uri, perm);
+            perm = new UriPermission(sourcePkg, targetPkg, targetUid, grantUri);
+            targetUris.put(grantUri, perm);
         }
 
         return perm;
     }
 
-    private final boolean checkUriPermissionLocked(Uri uri, int uid, final int modeFlags) {
+    private final boolean checkUriPermissionLocked(GrantUri grantUri, int uid,
+            final int modeFlags) {
         final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
         final int minStrength = persistable ? UriPermission.STRENGTH_PERSISTABLE
                 : UriPermission.STRENGTH_OWNED;
@@ -6128,7 +6141,7 @@
         if (perms == null) return false;
 
         // First look for exact match
-        final UriPermission exactPerm = perms.get(new GrantUri(uri, false));
+        final UriPermission exactPerm = perms.get(grantUri);
         if (exactPerm != null && exactPerm.getStrength(modeFlags) >= minStrength) {
             return true;
         }
@@ -6137,7 +6150,7 @@
         final int N = perms.size();
         for (int i = 0; i < N; i++) {
             final UriPermission perm = perms.valueAt(i);
-            if (perm.uri.prefix && uri.isPathPrefixMatch(perm.uri.uri)
+            if (perm.uri.prefix && grantUri.uri.isPathPrefixMatch(perm.uri.uri)
                     && perm.getStrength(modeFlags) >= minStrength) {
                 return true;
             }
@@ -6147,7 +6160,8 @@
     }
 
     @Override
-    public int checkUriPermission(Uri uri, int pid, int uid, final int modeFlags) {
+    public int checkUriPermission(Uri uri, int pid, int uid,
+            final int modeFlags, int userId) {
         enforceNotIsolatedCaller("checkUriPermission");
 
         // Another redirected-binder-call permissions check as in
@@ -6163,7 +6177,7 @@
             return PackageManager.PERMISSION_GRANTED;
         }
         synchronized (this) {
-            return checkUriPermissionLocked(uri, uid, modeFlags)
+            return checkUriPermissionLocked(new GrantUri(userId, uri, false), uid, modeFlags)
                     ? PackageManager.PERMISSION_GRANTED
                     : PackageManager.PERMISSION_DENIED;
         }
@@ -6178,30 +6192,31 @@
      * If you already know the uid of the target, you can supply it in
      * lastTargetUid else set that to -1.
      */
-    int checkGrantUriPermissionLocked(int callingUid, String targetPkg,
-            Uri uri, final int modeFlags, int lastTargetUid) {
+    int checkGrantUriPermissionLocked(int callingUid, String targetPkg, GrantUri grantUri,
+            final int modeFlags, int lastTargetUid) {
         if (!Intent.isAccessUriMode(modeFlags)) {
             return -1;
         }
 
         if (targetPkg != null) {
             if (DEBUG_URI_PERMISSION) Slog.v(TAG,
-                    "Checking grant " + targetPkg + " permission to " + uri);
+                    "Checking grant " + targetPkg + " permission to " + grantUri);
         }
         
         final IPackageManager pm = AppGlobals.getPackageManager();
 
         // If this is not a content: uri, we can't do anything with it.
-        if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+        if (!ContentResolver.SCHEME_CONTENT.equals(grantUri.uri.getScheme())) {
             if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
-                    "Can't grant URI permission for non-content URI: " + uri);
+                    "Can't grant URI permission for non-content URI: " + grantUri);
             return -1;
         }
 
-        final String authority = uri.getAuthority();
-        final ProviderInfo pi = getProviderInfoLocked(authority, UserHandle.getUserId(callingUid));
+        final String authority = grantUri.uri.getAuthority();
+        final ProviderInfo pi = getProviderInfoLocked(authority, grantUri.sourceUserId);
         if (pi == null) {
-            Slog.w(TAG, "No content provider found for permission check: " + uri.toSafeString());
+            Slog.w(TAG, "No content provider found for permission check: " +
+                    grantUri.uri.toSafeString());
             return -1;
         }
 
@@ -6221,10 +6236,10 @@
 
         if (targetUid >= 0) {
             // First...  does the target actually need this permission?
-            if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags)) {
+            if (checkHoldingPermissionsLocked(pm, pi, grantUri, targetUid, modeFlags)) {
                 // No need to grant the target this permission.
                 if (DEBUG_URI_PERMISSION) Slog.v(TAG,
-                        "Target " + targetPkg + " already has full permission to " + uri);
+                        "Target " + targetPkg + " already has full permission to " + grantUri);
                 return -1;
             }
         } else {
@@ -6250,14 +6265,14 @@
             throw new SecurityException("Provider " + pi.packageName
                     + "/" + pi.name
                     + " does not allow granting of Uri permissions (uri "
-                    + uri + ")");
+                    + grantUri + ")");
         }
         if (pi.uriPermissionPatterns != null) {
             final int N = pi.uriPermissionPatterns.length;
             boolean allowed = false;
             for (int i=0; i<N; i++) {
                 if (pi.uriPermissionPatterns[i] != null
-                        && pi.uriPermissionPatterns[i].match(uri.getPath())) {
+                        && pi.uriPermissionPatterns[i].match(grantUri.uri.getPath())) {
                     allowed = true;
                     break;
                 }
@@ -6266,35 +6281,35 @@
                 throw new SecurityException("Provider " + pi.packageName
                         + "/" + pi.name
                         + " does not allow granting of permission to path of Uri "
-                        + uri);
+                        + grantUri);
             }
         }
 
         // Third...  does the caller itself have permission to access
         // this uri?
-        if (callingUid != Process.myUid()) {
-            if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) {
+        if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
+            if (!checkHoldingPermissionsLocked(pm, pi, grantUri, callingUid, modeFlags)) {
                 // Require they hold a strong enough Uri permission
-                if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) {
+                if (!checkUriPermissionLocked(grantUri, callingUid, modeFlags)) {
                     throw new SecurityException("Uid " + callingUid
-                            + " does not have permission to uri " + uri);
+                            + " does not have permission to uri " + grantUri);
                 }
             }
         }
-
         return targetUid;
     }
 
     @Override
-    public int checkGrantUriPermission(int callingUid, String targetPkg,
-            Uri uri, final int modeFlags) {
+    public int checkGrantUriPermission(int callingUid, String targetPkg, Uri uri,
+            final int modeFlags, int userId) {
         enforceNotIsolatedCaller("checkGrantUriPermission");
         synchronized(this) {
-            return checkGrantUriPermissionLocked(callingUid, targetPkg, uri, modeFlags, -1);
+            return checkGrantUriPermissionLocked(callingUid, targetPkg,
+                    new GrantUri(userId, uri, false), modeFlags, -1);
         }
     }
 
-    void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg, Uri uri,
+    void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg, GrantUri grantUri,
             final int modeFlags, UriPermissionOwner owner) {
         if (!Intent.isAccessUriMode(modeFlags)) {
             return;
@@ -6305,36 +6320,40 @@
         // the target.
 
         if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
-                "Granting " + targetPkg + "/" + targetUid + " permission to " + uri);
+                "Granting " + targetPkg + "/" + targetUid + " permission to " + grantUri);
 
-        final String authority = uri.getAuthority();
-        final ProviderInfo pi = getProviderInfoLocked(authority, UserHandle.getUserId(targetUid));
+        final String authority = grantUri.uri.getAuthority();
+        final ProviderInfo pi = getProviderInfoLocked(authority, grantUri.sourceUserId);
         if (pi == null) {
-            Slog.w(TAG, "No content provider found for grant: " + uri.toSafeString());
+            Slog.w(TAG, "No content provider found for grant: " + grantUri.toSafeString());
             return;
         }
 
-        final boolean prefix = (modeFlags & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0;
+        if ((modeFlags & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0) {
+            grantUri.prefix = true;
+        }
         final UriPermission perm = findOrCreateUriPermissionLocked(
-                pi.packageName, targetPkg, targetUid, new GrantUri(uri, prefix));
+                pi.packageName, targetPkg, targetUid, grantUri);
         perm.grantModes(modeFlags, owner);
     }
 
-    void grantUriPermissionLocked(int callingUid, String targetPkg, Uri uri,
+    void grantUriPermissionLocked(int callingUid, String targetPkg, GrantUri grantUri,
             final int modeFlags, UriPermissionOwner owner) {
         if (targetPkg == null) {
             throw new NullPointerException("targetPkg");
         }
 
-        int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, uri, modeFlags, -1);
+        int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, modeFlags,
+                -1);
         if (targetUid < 0) {
             return;
         }
 
-        grantUriPermissionUncheckedLocked(targetUid, targetPkg, uri, modeFlags, owner);
+        grantUriPermissionUncheckedLocked(targetUid, targetPkg, grantUri, modeFlags,
+                owner);
     }
 
-    static class NeededUriGrants extends ArrayList<Uri> {
+    static class NeededUriGrants extends ArrayList<GrantUri> {
         final String targetPkg;
         final int targetUid;
         final int flags;
@@ -6371,13 +6390,14 @@
         }
 
         if (data != null) {
-            int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, data,
-                mode, needed != null ? needed.targetUid : -1);
+            GrantUri grantUri = GrantUri.resolve(UserHandle.getUserId(callingUid), data);
+            int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode,
+                    needed != null ? needed.targetUid : -1);
             if (targetUid > 0) {
                 if (needed == null) {
                     needed = new NeededUriGrants(targetPkg, targetUid, mode);
                 }
-                needed.add(data);
+                needed.add(grantUri);
             }
         }
         if (clip != null) {
@@ -6385,13 +6405,14 @@
                 Uri uri = clip.getItemAt(i).getUri();
                 if (uri != null) {
                     int targetUid = -1;
-                    targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, uri,
-                            mode, needed != null ? needed.targetUid : -1);
+                    GrantUri grantUri = GrantUri.resolve(UserHandle.getUserId(callingUid), uri);
+                    targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode,
+                            needed != null ? needed.targetUid : -1);
                     if (targetUid > 0) {
                         if (needed == null) {
                             needed = new NeededUriGrants(targetPkg, targetUid, mode);
                         }
-                        needed.add(uri);
+                        needed.add(grantUri);
                     }
                 } else {
                     Intent clipIntent = clip.getItemAt(i).getIntent();
@@ -6416,8 +6437,9 @@
             UriPermissionOwner owner) {
         if (needed != null) {
             for (int i=0; i<needed.size(); i++) {
+                GrantUri grantUri = needed.get(i);
                 grantUriPermissionUncheckedLocked(needed.targetUid, needed.targetPkg,
-                        needed.get(i), needed.flags, owner);
+                        grantUri, needed.flags, owner);
             }
         }
     }
@@ -6434,20 +6456,21 @@
     }
 
     @Override
-    public void grantUriPermission(IApplicationThread caller, String targetPkg,
-            Uri uri, final int modeFlags) {
+    public void grantUriPermission(IApplicationThread caller, String targetPkg, Uri uri,
+            final int modeFlags, int userId) {
         enforceNotIsolatedCaller("grantUriPermission");
+        GrantUri grantUri = new GrantUri(userId, uri, false);
         synchronized(this) {
             final ProcessRecord r = getRecordForAppLocked(caller);
             if (r == null) {
                 throw new SecurityException("Unable to find app for caller "
                         + caller
-                        + " when granting permission to uri " + uri);
+                        + " when granting permission to uri " + grantUri);
             }
             if (targetPkg == null) {
                 throw new IllegalArgumentException("null target");
             }
-            if (uri == null) {
+            if (grantUri == null) {
                 throw new IllegalArgumentException("null uri");
             }
 
@@ -6456,7 +6479,7 @@
                     | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
                     | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
 
-            grantUriPermissionLocked(r.uid, targetPkg, uri, modeFlags, null);
+            grantUriPermissionLocked(r.uid, targetPkg, grantUri, modeFlags, null);
         }
     }
 
@@ -6476,24 +6499,25 @@
         }
     }
 
-    private void revokeUriPermissionLocked(int callingUid, Uri uri, final int modeFlags) {
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Revoking all granted permissions to " + uri);
+    private void revokeUriPermissionLocked(int callingUid, GrantUri grantUri, final int modeFlags) {
+        if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Revoking all granted permissions to " + grantUri);
 
         final IPackageManager pm = AppGlobals.getPackageManager();
-        final String authority = uri.getAuthority();
-        final ProviderInfo pi = getProviderInfoLocked(authority, UserHandle.getUserId(callingUid));
+        final String authority = grantUri.uri.getAuthority();
+        final ProviderInfo pi = getProviderInfoLocked(authority, grantUri.sourceUserId);
         if (pi == null) {
-            Slog.w(TAG, "No content provider found for permission revoke: " + uri.toSafeString());
+            Slog.w(TAG, "No content provider found for permission revoke: "
+                    + grantUri.toSafeString());
             return;
         }
 
         // Does the caller have this permission on the URI?
-        if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) {
+        if (!checkHoldingPermissionsLocked(pm, pi, grantUri, callingUid, modeFlags)) {
             // Right now, if you are not the original owner of the permission,
             // you are not allowed to revoke it.
             //if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) {
                 throw new SecurityException("Uid " + callingUid
-                        + " does not have permission to uri " + uri);
+                        + " does not have permission to uri " + grantUri);
             //}
         }
 
@@ -6507,7 +6531,8 @@
 
             for (Iterator<UriPermission> it = perms.values().iterator(); it.hasNext();) {
                 final UriPermission perm = it.next();
-                if (perm.uri.uri.isPathPrefixMatch(uri)) {
+                if (perm.uri.sourceUserId == grantUri.sourceUserId
+                        && perm.uri.uri.isPathPrefixMatch(grantUri.uri)) {
                     if (DEBUG_URI_PERMISSION)
                         Slog.v(TAG,
                                 "Revoking " + perm.targetUid + " permission to " + perm.uri);
@@ -6532,8 +6557,8 @@
     }
 
     @Override
-    public void revokeUriPermission(IApplicationThread caller, Uri uri,
-            final int modeFlags) {
+    public void revokeUriPermission(IApplicationThread caller, Uri uri, final int modeFlags,
+            int userId) {
         enforceNotIsolatedCaller("revokeUriPermission");
         synchronized(this) {
             final ProcessRecord r = getRecordForAppLocked(caller);
@@ -6553,14 +6578,14 @@
 
             final IPackageManager pm = AppGlobals.getPackageManager();
             final String authority = uri.getAuthority();
-            final ProviderInfo pi = getProviderInfoLocked(authority, r.userId);
+            final ProviderInfo pi = getProviderInfoLocked(authority, userId);
             if (pi == null) {
                 Slog.w(TAG, "No content provider found for permission revoke: "
                         + uri.toSafeString());
                 return;
             }
 
-            revokeUriPermissionLocked(r.uid, uri, modeFlags);
+            revokeUriPermissionLocked(r.uid, new GrantUri(userId, uri, false), modeFlags);
         }
     }
 
@@ -6630,8 +6655,8 @@
     }
 
     @Override
-    public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg,
-            Uri uri, final int modeFlags) {
+    public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg, Uri uri,
+            final int modeFlags, int userId) {
         synchronized(this) {
             UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
             if (owner == null) {
@@ -6651,12 +6676,13 @@
                 throw new IllegalArgumentException("null uri");
             }
 
-            grantUriPermissionLocked(fromUid, targetPkg, uri, modeFlags, owner);
+            grantUriPermissionLocked(fromUid, targetPkg, new GrantUri(userId, uri, false),
+                    modeFlags, owner);
         }
     }
 
     @Override
-    public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode) {
+    public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId) {
         synchronized(this) {
             UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
             if (owner == null) {
@@ -6666,7 +6692,7 @@
             if (uri == null) {
                 owner.removeUriPermissionsLocked(mode);
             } else {
-                owner.removeUriPermissionLocked(uri, mode);
+                owner.removeUriPermissionLocked(new GrantUri(userId, uri, false), mode);
             }
         }
     }
@@ -6705,7 +6731,8 @@
             out.startTag(null, TAG_URI_GRANTS);
             for (UriPermission.Snapshot perm : persist) {
                 out.startTag(null, TAG_URI_GRANT);
-                writeIntAttribute(out, ATTR_USER_HANDLE, perm.userHandle);
+                writeIntAttribute(out, ATTR_SOURCE_USER_ID, perm.uri.sourceUserId);
+                writeIntAttribute(out, ATTR_TARGET_USER_ID, perm.targetUserId);
                 out.attribute(null, ATTR_SOURCE_PKG, perm.sourcePkg);
                 out.attribute(null, ATTR_TARGET_PKG, perm.targetPkg);
                 out.attribute(null, ATTR_URI, String.valueOf(perm.uri.uri));
@@ -6741,7 +6768,18 @@
                 final String tag = in.getName();
                 if (type == START_TAG) {
                     if (TAG_URI_GRANT.equals(tag)) {
-                        final int userHandle = readIntAttribute(in, ATTR_USER_HANDLE);
+                        final int sourceUserId;
+                        final int targetUserId;
+                        final int userHandle = readIntAttribute(in,
+                                ATTR_USER_HANDLE, UserHandle.USER_NULL);
+                        if (userHandle != UserHandle.USER_NULL) {
+                            // For backwards compatibility.
+                            sourceUserId = userHandle;
+                            targetUserId = userHandle;
+                        } else {
+                            sourceUserId = readIntAttribute(in, ATTR_SOURCE_USER_ID);
+                            targetUserId = readIntAttribute(in, ATTR_TARGET_USER_ID);
+                        }
                         final String sourcePkg = in.getAttributeValue(null, ATTR_SOURCE_PKG);
                         final String targetPkg = in.getAttributeValue(null, ATTR_TARGET_PKG);
                         final Uri uri = Uri.parse(in.getAttributeValue(null, ATTR_URI));
@@ -6751,17 +6789,18 @@
 
                         // Sanity check that provider still belongs to source package
                         final ProviderInfo pi = getProviderInfoLocked(
-                                uri.getAuthority(), userHandle);
+                                uri.getAuthority(), sourceUserId);
                         if (pi != null && sourcePkg.equals(pi.packageName)) {
                             int targetUid = -1;
                             try {
                                 targetUid = AppGlobals.getPackageManager()
-                                        .getPackageUid(targetPkg, userHandle);
+                                        .getPackageUid(targetPkg, targetUserId);
                             } catch (RemoteException e) {
                             }
                             if (targetUid != -1) {
                                 final UriPermission perm = findOrCreateUriPermissionLocked(
-                                        sourcePkg, targetPkg, targetUid, new GrantUri(uri, prefix));
+                                        sourcePkg, targetPkg, targetUid,
+                                        new GrantUri(sourceUserId, uri, prefix));
                                 perm.initPersistedModes(modeFlags, createdTime);
                             }
                         } else {
@@ -6783,7 +6822,7 @@
     }
 
     @Override
-    public void takePersistableUriPermission(Uri uri, final int modeFlags) {
+    public void takePersistableUriPermission(Uri uri, final int modeFlags, int userId) {
         enforceNotIsolatedCaller("takePersistableUriPermission");
 
         Preconditions.checkFlagsArgument(modeFlags,
@@ -6792,9 +6831,12 @@
         synchronized (this) {
             final int callingUid = Binder.getCallingUid();
             boolean persistChanged = false;
+            GrantUri grantUri = new GrantUri(userId, uri, false);
 
-            UriPermission exactPerm = findUriPermissionLocked(callingUid, new GrantUri(uri, false));
-            UriPermission prefixPerm = findUriPermissionLocked(callingUid, new GrantUri(uri, true));
+            UriPermission exactPerm = findUriPermissionLocked(callingUid,
+                    new GrantUri(userId, uri, false));
+            UriPermission prefixPerm = findUriPermissionLocked(callingUid,
+                    new GrantUri(userId, uri, true));
 
             final boolean exactValid = (exactPerm != null)
                     && ((modeFlags & exactPerm.persistableModeFlags) == modeFlags);
@@ -6803,7 +6845,7 @@
 
             if (!(exactValid || prefixValid)) {
                 throw new SecurityException("No persistable permission grants found for UID "
-                        + callingUid + " and Uri " + uri.toSafeString());
+                        + callingUid + " and Uri " + grantUri.toSafeString());
             }
 
             if (exactValid) {
@@ -6822,7 +6864,7 @@
     }
 
     @Override
-    public void releasePersistableUriPermission(Uri uri, final int modeFlags) {
+    public void releasePersistableUriPermission(Uri uri, final int modeFlags, int userId) {
         enforceNotIsolatedCaller("releasePersistableUriPermission");
 
         Preconditions.checkFlagsArgument(modeFlags,
@@ -6832,8 +6874,10 @@
             final int callingUid = Binder.getCallingUid();
             boolean persistChanged = false;
 
-            UriPermission exactPerm = findUriPermissionLocked(callingUid, new GrantUri(uri, false));
-            UriPermission prefixPerm = findUriPermissionLocked(callingUid, new GrantUri(uri, true));
+            UriPermission exactPerm = findUriPermissionLocked(callingUid,
+                    new GrantUri(userId, uri, false));
+            UriPermission prefixPerm = findUriPermissionLocked(callingUid,
+                    new GrantUri(userId, uri, true));
             if (exactPerm == null && prefixPerm == null) {
                 throw new SecurityException("No permission grants found for UID " + callingUid
                         + " and Uri " + uri.toSafeString());
@@ -7678,9 +7722,25 @@
      * in {@link ContentProvider}.
      */
     private final String checkContentProviderPermissionLocked(
-            ProviderInfo cpi, ProcessRecord r) {
+            ProviderInfo cpi, ProcessRecord r, int userId) {
         final int callingPid = (r != null) ? r.pid : Binder.getCallingPid();
         final int callingUid = (r != null) ? r.uid : Binder.getCallingUid();
+        final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
+        // Looking for cross-user grants before to enforce the typical cross-users permissions
+        if (userId != UserHandle.getUserId(callingUid)) {
+            if (perms != null) {
+                for (GrantUri grantUri : perms.keySet()) {
+                    if (grantUri.sourceUserId == userId) {
+                        String authority = grantUri.uri.getAuthority();
+                        if (authority.equals(cpi.authority)) {
+                            return null;
+                        }
+                    }
+                }
+            }
+        }
+        userId = handleIncomingUser(callingPid, callingUid, userId,
+                false, true, "checkContentProviderPermissionLocked", null);
         if (checkComponentPermission(cpi.readPermission, callingPid, callingUid,
                 cpi.applicationInfo.uid, cpi.exported)
                 == PackageManager.PERMISSION_GRANTED) {
@@ -7711,10 +7771,9 @@
             }
         }
 
-        final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
         if (perms != null) {
-            for (GrantUri uri : perms.keySet()) {
-                if (uri.uri.getAuthority().equals(cpi.authority)) {
+            for (GrantUri grantUri : perms.keySet()) {
+                if (grantUri.uri.getAuthority().equals(cpi.authority)) {
                     return null;
                 }
             }
@@ -7822,7 +7881,7 @@
             if (providerRunning) {
                 cpi = cpr.info;
                 String msg;
-                if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
+                if ((msg=checkContentProviderPermissionLocked(cpi, r, userId)) != null) {
                     throw new SecurityException(msg);
                 }
 
@@ -7910,7 +7969,7 @@
                 cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
 
                 String msg;
-                if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
+                if ((msg=checkContentProviderPermissionLocked(cpi, r, userId)) != null) {
                     throw new SecurityException(msg);
                 }
 
@@ -8076,6 +8135,7 @@
         return cpr != null ? cpr.newHolder(conn) : null;
     }
 
+    @Override
     public final ContentProviderHolder getContentProvider(
             IApplicationThread caller, String name, int userId, boolean stable) {
         enforceNotIsolatedCaller("getContentProvider");
@@ -8085,9 +8145,8 @@
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
-
-        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
-                false, true, "getContentProvider", null);
+        // The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
+        // with cross-user grant.
         return getContentProviderImpl(caller, name, null, stable, userId);
     }
 
@@ -9028,7 +9087,7 @@
     }
 
     @Override
-    public boolean convertToTranslucent(IBinder token) {
+    public boolean convertToTranslucent(IBinder token, ActivityOptions options) {
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
@@ -9037,7 +9096,7 @@
                     return false;
                 }
                 if (r.changeWindowTranslucency(false)) {
-                    r.task.stack.convertToTranslucent(r);
+                    r.task.stack.convertToTranslucent(r, options);
                     mWindowManager.setAppFullscreen(token, false);
                     mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
                     return true;
@@ -9050,6 +9109,24 @@
     }
 
     @Override
+    public ActivityOptions getActivityOptions(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r != null) {
+                    final ActivityOptions activityOptions = r.pendingOptions;
+                    r.pendingOptions = null;
+                    return activityOptions;
+                }
+                return null;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
     public void setImmersive(IBinder token, boolean immersive) {
         synchronized(this) {
             final ActivityRecord r = ActivityRecord.isInStackLocked(token);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 8391f79..9582ac7 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -347,7 +347,7 @@
             ActivityInfo aInfo, Configuration _configuration,
             ActivityRecord _resultTo, String _resultWho, int _reqCode,
             boolean _componentSpecified, ActivityStackSupervisor supervisor,
-            ActivityContainer container) {
+            ActivityContainer container, Bundle options) {
         service = _service;
         appToken = new Token(this);
         info = aInfo;
@@ -378,6 +378,9 @@
         hasBeenLaunched = false;
         mStackSupervisor = supervisor;
         mInitialActivityContainer = container;
+        if (options != null) {
+            pendingOptions = new ActivityOptions(options);
+        }
 
         // This starts out true, since the initial state of an activity
         // is that we have everything, and we shouldn't never consider it
@@ -711,6 +714,9 @@
                                         + pendingOptions.getThumbnail().getHeight()));
                     }
                     break;
+                default:
+                    Slog.e(TAG, "applyOptionsLocked: Unknown animationType=" + animationType);
+                    break;
             }
             pendingOptions = null;
         }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 2e439ae..c1e5e5b 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -205,6 +205,9 @@
     ActivityRecord mTranslucentActivityWaiting = null;
     ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent =
             new ArrayList<ActivityRecord>();
+    // Options passed from the caller of the convertToTranslucent to the activity that will
+    // appear below it.
+    ActivityOptions mReturningActivityOptions = null;
 
     /**
      * Set when we know we are going to be calling updateConfiguration()
@@ -1218,6 +1221,7 @@
                                     TAG, "Making visible and scheduling visibility: " + r);
                             try {
                                 if (mTranslucentActivityWaiting != null) {
+                                    r.updateOptionsLocked(mReturningActivityOptions);
                                     mUndrawnActivitiesBelowTopTranslucent.add(r);
                                 }
                                 setVisibile(r, true);
@@ -1295,9 +1299,10 @@
         }
     }
 
-    void convertToTranslucent(ActivityRecord r) {
+    void convertToTranslucent(ActivityRecord r, ActivityOptions options) {
         mTranslucentActivityWaiting = r;
         mUndrawnActivitiesBelowTopTranslucent.clear();
+        mReturningActivityOptions = options;
         mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT);
     }
 
@@ -1471,8 +1476,6 @@
         next.sleeping = false;
         mStackSupervisor.mWaitingVisibleActivities.remove(next);
 
-        next.updateOptionsLocked(options);
-
         if (DEBUG_SWITCH) Slog.v(TAG, "Resuming " + next);
 
         // If we are currently pausing an activity, then don't do anything
@@ -1916,7 +1919,6 @@
                         : AppTransition.TRANSIT_ACTIVITY_OPEN, keepCurTransition);
                 mNoAnimActivities.remove(r);
             }
-            r.updateOptionsLocked(options);
             mWindowManager.addAppToken(task.mActivities.indexOf(r),
                     r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                     (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId,
@@ -1967,13 +1969,14 @@
                     (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId,
                     r.info.configChanges);
             ActivityOptions.abort(options);
+            options = null;
         }
         if (VALIDATE_TOKENS) {
             validateAppTokensLocked();
         }
 
         if (doResume) {
-            mStackSupervisor.resumeTopActivitiesLocked();
+            mStackSupervisor.resumeTopActivitiesLocked(this, r, options);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 6f62a03..5d744e6 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1022,14 +1022,12 @@
             }
 
             app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP);
-            Bundle options = (r.pendingOptions == null) ? null : r.pendingOptions.toBundle();
-            r.clearOptionsLocked();
             app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                     System.identityHashCode(r), r.info,
                     new Configuration(mService.mConfiguration), r.compat, r.task.voiceInteractor,
                     app.repProcState, r.icicle, r.persistentState, results, newIntents, !andResume,
-                    mService.isNextTransitionForward(), profileFile, profileFd, profileAutoStop,
-                    options);
+                    mService.isNextTransitionForward(), profileFile, profileFd, profileAutoStop
+            );
 
             if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
                 // This may be a heavy-weight process!  Note that the package
@@ -1325,7 +1323,7 @@
 
         ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
                 intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
-                requestCode, componentSpecified, this, container);
+                requestCode, componentSpecified, this, container, options);
         if (outActivity != null) {
             outActivity[0] = r;
         }
diff --git a/services/core/java/com/android/server/am/UriPermission.java b/services/core/java/com/android/server/am/UriPermission.java
index 4970b8d..284086d 100644
--- a/services/core/java/com/android/server/am/UriPermission.java
+++ b/services/core/java/com/android/server/am/UriPermission.java
@@ -44,7 +44,7 @@
     public static final int STRENGTH_GLOBAL = 2;
     public static final int STRENGTH_PERSISTABLE = 3;
 
-    final int userHandle;
+    final int targetUserId;
     final String sourcePkg;
     final String targetPkg;
 
@@ -86,7 +86,7 @@
     private String stringName;
 
     UriPermission(String sourcePkg, String targetPkg, int targetUid, GrantUri uri) {
-        this.userHandle = UserHandle.getUserId(targetUid);
+        this.targetUserId = UserHandle.getUserId(targetUid);
         this.sourcePkg = sourcePkg;
         this.targetPkg = targetPkg;
         this.targetUid = targetUid;
@@ -307,7 +307,7 @@
 
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix);
-        pw.print("userHandle=" + userHandle);
+        pw.print("targetUserId=" + targetUserId);
         pw.print(" sourcePkg=" + sourcePkg);
         pw.println(" targetPkg=" + targetPkg);
 
@@ -352,7 +352,7 @@
      * {@link UriPermission#persistedModeFlags} state.
      */
     public static class Snapshot {
-        final int userHandle;
+        final int targetUserId;
         final String sourcePkg;
         final String targetPkg;
         final GrantUri uri;
@@ -360,7 +360,7 @@
         final long persistedCreateTime;
 
         private Snapshot(UriPermission perm) {
-            this.userHandle = perm.userHandle;
+            this.targetUserId = perm.targetUserId;
             this.sourcePkg = perm.sourcePkg;
             this.targetPkg = perm.targetPkg;
             this.uri = perm.uri;
diff --git a/services/core/java/com/android/server/am/UriPermissionOwner.java b/services/core/java/com/android/server/am/UriPermissionOwner.java
index 65d7047..ae83940 100644
--- a/services/core/java/com/android/server/am/UriPermissionOwner.java
+++ b/services/core/java/com/android/server/am/UriPermissionOwner.java
@@ -70,13 +70,13 @@
         removeUriPermissionLocked(null, mode);
     }
 
-    void removeUriPermissionLocked(Uri uri, int mode) {
+    void removeUriPermissionLocked(ActivityManagerService.GrantUri grantUri, int mode) {
         if ((mode & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0
                 && mReadPerms != null) {
             Iterator<UriPermission> it = mReadPerms.iterator();
             while (it.hasNext()) {
                 UriPermission perm = it.next();
-                if (uri == null || uri.equals(perm.uri)) {
+                if (grantUri == null || grantUri.equals(perm.uri)) {
                     perm.removeReadOwner(this);
                     service.removeUriPermissionIfNeededLocked(perm);
                     it.remove();
@@ -91,7 +91,7 @@
             Iterator<UriPermission> it = mWritePerms.iterator();
             while (it.hasNext()) {
                 UriPermission perm = it.next();
-                if (uri == null || uri.equals(perm.uri)) {
+                if (grantUri == null || grantUri.equals(perm.uri)) {
                     perm.removeWriteOwner(this);
                     service.removeUriPermissionIfNeededLocked(perm);
                     it.remove();
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index f47d66d..15e3e89 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -23,6 +23,7 @@
 import android.content.BroadcastReceiver;
 import android.content.ClipData;
 import android.content.ClipDescription;
+import android.content.ContentProvider;
 import android.content.IClipboard;
 import android.content.IOnPrimaryClipChangedListener;
 import android.content.Context;
@@ -255,7 +256,8 @@
         long ident = Binder.clearCallingIdentity();
         try {
             // This will throw SecurityException for us.
-            mAm.checkGrantUriPermission(uid, null, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            mAm.checkGrantUriPermission(uid, null, ContentProvider.getUriWithoutUserId(uri),
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION, resolveUserId(uri, uid));
         } catch (RemoteException e) {
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -282,8 +284,10 @@
     private final void grantUriLocked(Uri uri, String pkg) {
         long ident = Binder.clearCallingIdentity();
         try {
-            mAm.grantUriPermissionFromOwner(mPermissionOwner, Process.myUid(), pkg, uri,
-                    Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            mAm.grantUriPermissionFromOwner(mPermissionOwner, Process.myUid(), pkg,
+                    ContentProvider.getUriWithoutUserId(uri),
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION,
+                    resolveUserId(uri, Process.myUid()));
         } catch (RemoteException e) {
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -331,9 +335,10 @@
     private final void revokeUriLocked(Uri uri) {
         long ident = Binder.clearCallingIdentity();
         try {
-            mAm.revokeUriPermissionFromOwner(mPermissionOwner, uri,
-                    Intent.FLAG_GRANT_READ_URI_PERMISSION
-                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+            mAm.revokeUriPermissionFromOwner(mPermissionOwner,
+                    ContentProvider.getUriWithoutUserId(uri),
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+                    resolveUserId(uri, Process.myUid()));
         } catch (RemoteException e) {
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -361,4 +366,8 @@
             revokeItemLocked(clipboard.primaryClip.getItemAt(i));
         }
     }
+
+    private final int resolveUserId(Uri uri, int uid) {
+        return ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(uid));
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgent.java b/services/core/java/com/android/server/connectivity/NetworkAgent.java
new file mode 100644
index 0000000..a0af06d
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/NetworkAgent.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.net.Network;
+import android.net.NetworkInfo;
+import android.net.NetworkRequest;
+
+import com.android.internal.util.AsyncChannel;
+
+import java.util.ArrayList;
+
+/**
+ * {@hide}
+ */
+public class NetworkAgent {
+    public NetworkInfo networkInfo;
+    public Network network;
+    public final ArrayList<NetworkRequest> networkRequests = new ArrayList<NetworkRequest>();
+    public final ArrayList<NetworkRequest> networkListens = new ArrayList<NetworkRequest>();
+    public final AsyncChannel asyncChannel;
+
+    public NetworkAgent(AsyncChannel ac) {
+        asyncChannel = ac;
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index 0749f24..63178eb 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -25,6 +25,7 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.net.ProxyInfo;
+import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -32,7 +33,6 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.Settings;
-import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -71,7 +71,7 @@
     public static final String KEY_PROXY = "keyProxy";
     private String mCurrentPac;
     @GuardedBy("mProxyLock")
-    private String mPacUrl;
+    private Uri mPacUrl;
 
     private AlarmManager mAlarmManager;
     @GuardedBy("mProxyLock")
@@ -100,7 +100,7 @@
         public void run() {
             String file;
             synchronized (mProxyLock) {
-                if (mPacUrl == null) return;
+                if (Uri.EMPTY.equals(mPacUrl)) return;
                 try {
                     file = get(mPacUrl);
                 } catch (IOException ioe) {
@@ -158,13 +158,13 @@
      * @return Returns true when the broadcast should not be sent
      */
     public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
-        if (proxy.getPacFileUrl() != null) {
+        if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
             if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
                 // Allow to send broadcast, nothing to do.
                 return false;
             }
             synchronized (mProxyLock) {
-                mPacUrl = proxy.getPacFileUrl().toString();
+                mPacUrl = proxy.getPacFileUrl();
             }
             mCurrentDelay = DELAY_1;
             mHasSentBroadcast = false;
@@ -196,8 +196,8 @@
      *
      * @throws IOException
      */
-    private static String get(String urlString) throws IOException {
-        URL url = new URL(urlString);
+    private static String get(Uri pacUri) throws IOException {
+        URL url = new URL(pacUri.toString());
         URLConnection urlConnection = url.openConnection(java.net.Proxy.NO_PROXY);
         return new String(Streams.readFully(urlConnection.getInputStream()));
     }
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 5fa1584..af0c56b 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -48,8 +48,7 @@
     private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
 
     // If true, enables the use of the screen auto-brightness adjustment setting.
-    private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT =
-            PowerManager.useScreenAutoBrightnessAdjustmentFeature();
+    private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT = true;
 
     // The maximum range of gamma adjustment possible using the screen
     // auto-brightness adjustment setting.
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index d074565..007032e 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -16,8 +16,13 @@
 
 package com.android.server.notification;
 
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
@@ -30,6 +35,7 @@
 import android.service.notification.IConditionListener;
 import android.service.notification.IConditionProvider;
 import android.service.notification.ZenModeConfig;
+import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -39,6 +45,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Date;
 
 public class ConditionProviders extends ManagedServices {
     private static final Condition[] NO_CONDITIONS = new Condition[0];
@@ -47,6 +54,7 @@
     private final ArrayMap<IBinder, IConditionListener> mListeners
             = new ArrayMap<IBinder, IConditionListener>();
     private final ArrayList<ConditionRecord> mRecords = new ArrayList<ConditionRecord>();
+    private final CountdownConditionHelper mCountdownHelper = new CountdownConditionHelper();
 
     public ConditionProviders(Context context, Handler handler,
             UserProfiles userProfiles, ZenModeHelper zenModeHelper) {
@@ -267,6 +275,7 @@
                 }
             }
         }
+        mCountdownHelper.setZenModeCondition(conditionId);
     }
 
     private void subscribeLocked(ConditionRecord r) {
@@ -434,6 +443,68 @@
         mZenModeHelper.setConfig(config);
     }
 
+    private final class CountdownConditionHelper extends BroadcastReceiver {
+        private static final String ACTION = "CountdownConditionHelper";
+        private static final int REQUEST_CODE = 100;
+        private static final String EXTRA_TIME = "time";
+
+        private long mCurrent;
+
+        public CountdownConditionHelper() {
+            mContext.registerReceiver(this, new IntentFilter(ACTION));
+        }
+
+        public void setZenModeCondition(Uri conditionId) {
+            final long time = tryParseCondition(conditionId);
+            final AlarmManager alarms = (AlarmManager)
+                    mContext.getSystemService(Context.ALARM_SERVICE);
+            final Intent intent = new Intent(ACTION).putExtra(EXTRA_TIME, time)
+                    .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+            final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, REQUEST_CODE,
+                    intent, PendingIntent.FLAG_UPDATE_CURRENT);
+            alarms.cancel(pendingIntent);
+            mCurrent = time;
+            if (time > 0) {
+                final long now = System.currentTimeMillis();
+                final CharSequence span =
+                        DateUtils.getRelativeTimeSpanString(time, now, DateUtils.MINUTE_IN_MILLIS);
+                Slog.d(TAG, String.format("Scheduling %s for %s, %s in the future (%s), now=%s",
+                        ACTION, ts(time), time - now, span, ts(now)));
+                alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
+            }
+        }
+
+        private String ts(long time) {
+            return new Date(time) + " (" + time + ")";
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (ACTION.equals(intent.getAction())) {
+                final long time = intent.getLongExtra(EXTRA_TIME, 0);
+                Slog.d(TAG, "Countdown condition fired. time=" + time + " mCurrent=" + mCurrent);
+                if (time > 0 && time == mCurrent) {
+                    // countdown condition is still the manual condition, leave zen
+                    mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF);
+                    ConditionProviders.this.setZenModeCondition(null);
+                }
+            }
+        }
+
+        private long tryParseCondition(Uri conditionId) {
+            // condition://android/countdown/1399917958951
+            if (!Condition.isValidId(conditionId, "android")) return 0;
+            if (conditionId.getPathSegments().size() != 2
+                    || !"countdown".equals(conditionId.getPathSegments().get(0))) return 0;
+            try {
+                return Long.parseLong(conditionId.getPathSegments().get(1));
+            } catch (RuntimeException e) {
+                Slog.w(TAG, "Error parsing countdown condition: " + conditionId, e);
+                return 0;
+            }
+        }
+    }
+
     private class ZenModeHelperCallback extends ZenModeHelper.Callback {
         @Override
         void onConfigChanged() {
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index d34b09c..584145f 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -66,7 +66,7 @@
 
     private static final String ENABLED_SERVICES_SEPARATOR = ":";
 
-    private final Context mContext;
+    protected final Context mContext;
     protected final Object mMutex;
     private final UserProfiles mUserProfiles;
     private final SettingsObserver mSettingsObserver;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7a4f951..f1c05ac 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1401,7 +1401,12 @@
         @Override
         public void setZenModeCondition(Uri conditionId) {
             enforceSystemOrSystemUI("INotificationManager.setZenModeCondition");
-            mConditionProviders.setZenModeCondition(conditionId);
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                mConditionProviders.setZenModeCondition(conditionId);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
 
         @Override
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index f7bec6e..39f228f 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -28,6 +28,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Binder;
@@ -35,6 +36,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.print.IPrintDocumentAdapter;
 import android.print.IPrintJobStateChangeListener;
 import android.print.IPrintManager;
@@ -91,18 +93,23 @@
         private static final String EXTRA_PRINT_SERVICE_COMPONENT_NAME =
                 "EXTRA_PRINT_SERVICE_COMPONENT_NAME";
 
+        private static final int BACKGROUND_USER_ID = -10;
+
         private final Object mLock = new Object();
 
         private final Context mContext;
 
+        private final UserManager mUserManager;
+
         private final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
 
         private int mCurrentUserId = UserHandle.USER_OWNER;
 
         PrintManagerImpl(Context context) {
             mContext = context;
+            mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
             registerContentObservers();
-            registerBoradcastReceivers();
+            registerBroadcastReceivers();
         }
 
         public void systemRunning() {
@@ -125,11 +132,17 @@
         @Override
         public Bundle print(String printJobName, IPrintDocumentAdapter adapter,
                 PrintAttributes attributes, String packageName, int appId, int userId) {
-            final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
-            String resolvedPackageName = resolveCallingPackageNameEnforcingSecurity(packageName);
+            final int resolvedAppId;
             final UserState userState;
+            final String resolvedPackageName;
             synchronized (mLock) {
+                // Only the current group members can start new print jobs.
+                if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) {
+                    return null;
+                }
+                resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+                resolvedPackageName = resolveCallingPackageNameEnforcingSecurity(packageName);
                 userState = getOrCreateUserStateLocked(resolvedUserId);
             }
             final long identity = Binder.clearCallingIdentity();
@@ -143,10 +156,15 @@
 
         @Override
         public List<PrintJobInfo> getPrintJobInfos(int appId, int userId) {
-            final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final int resolvedAppId;
             final UserState userState;
             synchronized (mLock) {
+                // Only the current group members can query for state of print jobs.
+                if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) {
+                    return null;
+                }
+                resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
                 userState = getOrCreateUserStateLocked(resolvedUserId);
             }
             final long identity = Binder.clearCallingIdentity();
@@ -159,10 +177,15 @@
 
         @Override
         public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId, int userId) {
-            final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final int resolvedAppId;
             final UserState userState;
             synchronized (mLock) {
+                // Only the current group members can query for state of a print job.
+                if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) {
+                    return null;
+                }
+                resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
                 userState = getOrCreateUserStateLocked(resolvedUserId);
             }
             final long identity = Binder.clearCallingIdentity();
@@ -175,10 +198,15 @@
 
         @Override
         public void cancelPrintJob(PrintJobId printJobId, int appId, int userId) {
-            final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final int resolvedAppId;
             final UserState userState;
             synchronized (mLock) {
+                // Only the current group members can cancel a print job.
+                if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) {
+                    return;
+                }
+                resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
                 userState = getOrCreateUserStateLocked(resolvedUserId);
             }
             final long identity = Binder.clearCallingIdentity();
@@ -191,10 +219,15 @@
 
         @Override
         public void restartPrintJob(PrintJobId printJobId, int appId, int userId) {
-            final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final int resolvedAppId;
             final UserState userState;
             synchronized (mLock) {
+                // Only the current group members can restart a print job.
+                if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) {
+                    return;
+                }
+                resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
                 userState = getOrCreateUserStateLocked(resolvedUserId);
             }
             final long identity = Binder.clearCallingIdentity();
@@ -210,6 +243,10 @@
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
+                // Only the current group members can get enabled services.
+                if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) {
+                    return null;
+                }
                 userState = getOrCreateUserStateLocked(resolvedUserId);
             }
             final long identity = Binder.clearCallingIdentity();
@@ -225,6 +262,10 @@
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
+                // Only the current group members can get installed services.
+                if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) {
+                    return null;
+                }
                 userState = getOrCreateUserStateLocked(resolvedUserId);
             }
             final long identity = Binder.clearCallingIdentity();
@@ -241,6 +282,10 @@
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
+                // Only the current group members can create a discovery session.
+                if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) {
+                    return;
+                }
                 userState = getOrCreateUserStateLocked(resolvedUserId);
             }
             final long identity = Binder.clearCallingIdentity();
@@ -257,6 +302,10 @@
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
+                // Only the current group members can destroy a discovery session.
+                if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) {
+                    return;
+                }
                 userState = getOrCreateUserStateLocked(resolvedUserId);
             }
             final long identity = Binder.clearCallingIdentity();
@@ -273,6 +322,10 @@
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
+                // Only the current group members can start discovery.
+                if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) {
+                    return;
+                }
                 userState = getOrCreateUserStateLocked(resolvedUserId);
             }
             final long identity = Binder.clearCallingIdentity();
@@ -288,6 +341,10 @@
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
+                // Only the current group members can stop discovery.
+                if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) {
+                    return;
+                }
                 userState = getOrCreateUserStateLocked(resolvedUserId);
             }
             final long identity = Binder.clearCallingIdentity();
@@ -303,6 +360,10 @@
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
+                // Only the current group members can validate printers.
+                if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) {
+                    return;
+                }
                 userState = getOrCreateUserStateLocked(resolvedUserId);
             }
             final long identity = Binder.clearCallingIdentity();
@@ -318,6 +379,10 @@
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
+                // Only the current group members can start printer tracking.
+                if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) {
+                    return;
+                }
                 userState = getOrCreateUserStateLocked(resolvedUserId);
             }
             final long identity = Binder.clearCallingIdentity();
@@ -333,6 +398,10 @@
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
+                // Only the current group members can stop printer tracking.
+                if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) {
+                    return;
+                }
                 userState = getOrCreateUserStateLocked(resolvedUserId);
             }
             final long identity = Binder.clearCallingIdentity();
@@ -347,9 +416,14 @@
         public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener,
                 int appId, int userId) throws RemoteException {
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
-            final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+            final int resolvedAppId;
             final UserState userState;
             synchronized (mLock) {
+                // Only the current group members can add a print job listener.
+                if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) {
+                    return;
+                }
+                resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
                 userState = getOrCreateUserStateLocked(resolvedUserId);
             }
             final long identity = Binder.clearCallingIdentity();
@@ -366,6 +440,10 @@
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
+                // Only the current group members can remove a print job listener.
+                if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) {
+                    return;
+                }
                 userState = getOrCreateUserStateLocked(resolvedUserId);
             }
             final long identity = Binder.clearCallingIdentity();
@@ -421,11 +499,14 @@
                     false, observer, UserHandle.USER_ALL);
         }
 
-        private void registerBoradcastReceivers() {
+        private void registerBroadcastReceivers() {
             PackageMonitor monitor = new PackageMonitor() {
                 @Override
                 public void onPackageModified(String packageName) {
                     synchronized (mLock) {
+                        // A background user/profile's print jobs are running but there is
+                        // no UI shown. Hence, if the packages of such a user change we need
+                        // to handle it as the change may affect ongoing print jobs.
                         boolean servicesChanged = false;
                         UserState userState = getOrCreateUserStateLocked(getChangingUserId());
                         Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
@@ -444,6 +525,9 @@
                 @Override
                 public void onPackageRemoved(String packageName, int uid) {
                     synchronized (mLock) {
+                        // A background user/profile's print jobs are running but there is
+                        // no UI shown. Hence, if the packages of such a user change we need
+                        // to handle it as the change may affect ongoing print jobs.
                         boolean servicesRemoved = false;
                         UserState userState = getOrCreateUserStateLocked(getChangingUserId());
                         Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
@@ -467,6 +551,9 @@
                 public boolean onHandleForceStop(Intent intent, String[] stoppedPackages,
                         int uid, boolean doit) {
                     synchronized (mLock) {
+                        // A background user/profile's print jobs are running but there is
+                        // no UI shown. Hence, if the packages of such a user change we need
+                        // to handle it as the change may affect ongoing print jobs.
                         UserState userState = getOrCreateUserStateLocked(getChangingUserId());
                         boolean stoppedSomePackages = false;
                         Iterator<ComponentName> iterator = userState.getEnabledServices()
@@ -493,6 +580,9 @@
 
                 @Override
                 public void onPackageAdded(String packageName, int uid) {
+                    // A background user/profile's print jobs are running but there is
+                    // no UI shown. Hence, if the packages of such a user change we need
+                    // to handle it as the change may affect ongoing print jobs.
                     Intent intent = new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
                     intent.setPackage(packageName);
 
@@ -596,6 +686,23 @@
             }
         }
 
+        private int resolveCallingProfileParentLocked(int userId) {
+            if (userId != mCurrentUserId) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    UserInfo parent = mUserManager.getProfileParent(userId);
+                    if (parent != null) {
+                        return parent.getUserHandle().getIdentifier();
+                    } else {
+                        return BACKGROUND_USER_ID;
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+            return userId;
+        }
+
         private int resolveCallingAppEnforcingPermissions(int appId) {
             final int callingUid = Binder.getCallingUid();
             if (callingUid == 0 || callingUid == Process.SYSTEM_UID
diff --git a/tools/aapt/ApkBuilder.h b/tools/aapt/ApkBuilder.h
index a4b7d4a..db23c84 100644
--- a/tools/aapt/ApkBuilder.h
+++ b/tools/aapt/ApkBuilder.h
@@ -55,6 +55,10 @@
         return mSplits;
     }
 
+    android::sp<ApkSplit> getBaseSplit() {
+        return mSplits[0];
+    }
+
     void print() const;
 
 private:
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 0360200..dca25e5 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -2035,10 +2035,16 @@
     return (result != NO_ERROR);
 }
 
-static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder) {
+static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
     const size_t numDirs = dir->getDirs().size();
     for (size_t i = 0; i < numDirs; i++) {
-        status_t err = addResourcesToBuilder(dir->getDirs().valueAt(i), builder);
+        bool ignore = ignoreConfig;
+        const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
+        const char* dirStr = subDir->getLeaf().string();
+        if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
+            ignore = true;
+        }
+        status_t err = addResourcesToBuilder(subDir, builder, ignore);
         if (err != NO_ERROR) {
             return err;
         }
@@ -2049,7 +2055,12 @@
         sp<AaptGroup> gp = dir->getFiles().valueAt(i);
         const size_t numConfigs = gp->getFiles().size();
         for (size_t j = 0; j < numConfigs; j++) {
-            status_t err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
+            status_t err = NO_ERROR;
+            if (ignoreConfig) {
+                err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
+            } else {
+                err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
+            }
             if (err != NO_ERROR) {
                 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
                         gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 9787432..4af73cf 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -183,6 +183,7 @@
         SessionParams params = getParams();
         BridgeContext context = getContext();
 
+
         RenderResources resources = getParams().getResources();
         DisplayMetrics metrics = getContext().getMetrics();
 
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 813a895..59979ce 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -137,6 +137,7 @@
         "android.text.format.DateFormat#is24HourFormat",
         "android.view.Choreographer#getRefreshRate",
         "android.view.Display#updateDisplayInfoLocked",
+        "android.view.Display#getWindowManager",
         "android.view.LayoutInflater#rInflate",
         "android.view.LayoutInflater#parseInclude",
         "android.view.View#isInEditMode",