Merge "Network notifications: revamp keying scheme"
diff --git a/Android.mk b/Android.mk
index e17fd0b..36a35a1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -774,9 +774,6 @@
 # include definition of libcore_to_document
 include libcore/Docs.mk
 
-# include definition of junit_to_document
-include external/junit/Common.mk
-
 non_base_dirs := \
 	../opt/telephony/src/java/android/provider \
 	../opt/telephony/src/java/android/telephony \
@@ -817,8 +814,7 @@
 # Common sources for doc check and api check
 common_src_files := \
 	$(call find-other-html-files, $(html_dirs)) \
-	$(addprefix ../../, $(libcore_to_document)) \
-	$(addprefix ../../external/junit/, $(junit_to_document))
+	$(addprefix ../../, $(libcore_to_document))
 
 # These are relative to frameworks/base
 framework_docs_LOCAL_SRC_FILES := \
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/PathPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/PathPerfTest.java
new file mode 100644
index 0000000..7a49b4f
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/PathPerfTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 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.graphics.perftests;
+
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+@LargeTest
+public class PathPerfTest {
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void testReset() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Path path = new Path();
+        while (state.keepRunning()) {
+            path.reset();
+        }
+    }
+
+    @Test
+    public void testAddReset() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Path path = new Path();
+        while (state.keepRunning()) {
+            path.addRect(0, 0, 100, 100, Path.Direction.CW);
+            path.reset();
+        }
+    }
+
+    @Test
+    public void testRewind() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Path path = new Path();
+        while (state.keepRunning()) {
+            path.rewind();
+        }
+    }
+
+    @Test
+    public void testAddRewind() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Path path = new Path();
+        while (state.keepRunning()) {
+            path.addRect(0, 0, 100, 100, Path.Direction.CW);
+            path.rewind();
+        }
+    }
+
+    @Test
+    public void testIsEmpty() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Path path = new Path();
+        path.addRect(0, 0, 100, 100, Path.Direction.CW);
+        while (state.keepRunning()) {
+            path.isEmpty();
+        }
+    }
+
+    @Test
+    public void testIsConvex() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Path path = new Path();
+        path.addRect(0, 0, 100, 100, Path.Direction.CW);
+        while (state.keepRunning()) {
+            path.isConvex();
+        }
+    }
+
+    @Test
+    public void testGetSetFillType() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Path path = new Path();
+        path.addRect(0, 0, 100, 100, Path.Direction.CW);
+        while (state.keepRunning()) {
+            path.setFillType(Path.FillType.EVEN_ODD);
+            path.getFillType();
+        }
+    }
+
+    @Test
+    public void testIsRect() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Path path = new Path();
+        path.addRect(0, 0, 100, 100, Path.Direction.CW);
+        final RectF outRect = new RectF();
+        while (state.keepRunning()) {
+            path.isRect(outRect);
+        }
+    }
+}
diff --git a/api/current.txt b/api/current.txt
index 460b99c..4a5e786 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -750,6 +750,8 @@
     field public static final int keyWidth = 16843325; // 0x101023d
     field public static final int keyboardLayout = 16843691; // 0x10103ab
     field public static final int keyboardMode = 16843341; // 0x101024d
+    field public static final int keyboardNavigationCluster = 16844096; // 0x1010540
+    field public static final int keyboardNavigationSection = 16844097; // 0x1010541
     field public static final int keycode = 16842949; // 0x10100c5
     field public static final int killAfterRestore = 16843420; // 0x101029c
     field public static final int label = 16842753; // 0x1010001
@@ -893,11 +895,13 @@
     field public static final int negativeButtonText = 16843254; // 0x10101f6
     field public static final int nestedScrollingEnabled = 16843830; // 0x1010436
     field public static final int networkSecurityConfig = 16844071; // 0x1010527
+    field public static final int nextClusterForward = 16844098; // 0x1010542
     field public static final int nextFocusDown = 16842980; // 0x10100e4
     field public static final int nextFocusForward = 16843580; // 0x101033c
     field public static final int nextFocusLeft = 16842977; // 0x10100e1
     field public static final int nextFocusRight = 16842978; // 0x10100e2
     field public static final int nextFocusUp = 16842979; // 0x10100e3
+    field public static final int nextSectionForward = 16844099; // 0x1010543
     field public static final int noHistory = 16843309; // 0x101022d
     field public static final int normalScreens = 16843397; // 0x1010285
     field public static final int notificationTimeout = 16843651; // 0x1010383
@@ -11787,6 +11791,7 @@
     enum_constant public static final android.graphics.Bitmap.Config ALPHA_8;
     enum_constant public static final deprecated android.graphics.Bitmap.Config ARGB_4444;
     enum_constant public static final android.graphics.Bitmap.Config ARGB_8888;
+    enum_constant public static final android.graphics.Bitmap.Config HARDWARE;
     enum_constant public static final android.graphics.Bitmap.Config RGB_565;
   }
 
@@ -12247,12 +12252,6 @@
     enum_constant public static final android.graphics.Interpolator.Result NORMAL;
   }
 
-  public deprecated class LayerRasterizer extends android.graphics.Rasterizer {
-    ctor public LayerRasterizer();
-    method public void addLayer(android.graphics.Paint, float, float);
-    method public void addLayer(android.graphics.Paint);
-  }
-
   public class LightingColorFilter extends android.graphics.ColorFilter {
     ctor public LightingColorFilter(int, int);
   }
@@ -12414,7 +12413,6 @@
     method public int getOffsetForAdvance(char[], int, int, int, int, boolean, float);
     method public int getOffsetForAdvance(java.lang.CharSequence, int, int, int, int, boolean, float);
     method public android.graphics.PathEffect getPathEffect();
-    method public deprecated android.graphics.Rasterizer getRasterizer();
     method public float getRunAdvance(char[], int, int, int, int, boolean, int);
     method public float getRunAdvance(java.lang.CharSequence, int, int, int, int, boolean, int);
     method public android.graphics.Shader getShader();
@@ -12471,7 +12469,6 @@
     method public void setLinearText(boolean);
     method public android.graphics.MaskFilter setMaskFilter(android.graphics.MaskFilter);
     method public android.graphics.PathEffect setPathEffect(android.graphics.PathEffect);
-    method public deprecated android.graphics.Rasterizer setRasterizer(android.graphics.Rasterizer);
     method public android.graphics.Shader setShader(android.graphics.Shader);
     method public void setShadowLayer(float, float, float, int);
     method public void setStrikeThruText(boolean);
@@ -12778,10 +12775,6 @@
     ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode);
   }
 
-  public deprecated class Rasterizer {
-    ctor public Rasterizer();
-  }
-
   public final class Rect implements android.os.Parcelable {
     ctor public Rect();
     ctor public Rect(int, int, int, int);
@@ -22616,6 +22609,7 @@
     method public android.content.ComponentName getServiceComponent();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public boolean isConnected();
+    method public void search(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SearchCallback);
     method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void unsubscribe(java.lang.String);
@@ -22651,6 +22645,12 @@
     field public static final int FLAG_PLAYABLE = 2; // 0x2
   }
 
+  public static abstract class MediaBrowser.SearchCallback {
+    ctor public MediaBrowser.SearchCallback();
+    method public void onError(java.lang.String, android.os.Bundle);
+    method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
+  }
+
   public static abstract class MediaBrowser.SubscriptionCallback {
     ctor public MediaBrowser.SubscriptionCallback();
     method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
@@ -29721,6 +29721,7 @@
     method public deprecated void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
     method public static boolean supportsMultipleUsers();
     field public static final java.lang.String ALLOW_PARENT_PROFILE_APP_LINKING = "allow_parent_profile_app_linking";
+    field public static final java.lang.String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
     field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
     field public static final java.lang.String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
     field public static final java.lang.String DISALLOW_APPS_CONTROL = "no_control_apps";
@@ -29745,6 +29746,7 @@
     field public static final java.lang.String DISALLOW_NETWORK_RESET = "no_network_reset";
     field public static final java.lang.String DISALLOW_OUTGOING_BEAM = "no_outgoing_beam";
     field public static final java.lang.String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls";
+    field public static final java.lang.String DISALLOW_REMOVE_MANAGED_PROFILE = "no_remove_managed_profile";
     field public static final java.lang.String DISALLOW_REMOVE_USER = "no_remove_user";
     field public static final java.lang.String DISALLOW_SAFE_BOOT = "no_safe_boot";
     field public static final java.lang.String DISALLOW_SET_USER_ICON = "no_set_user_icon";
@@ -35082,6 +35084,7 @@
     method public abstract void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
     method public void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>, android.os.Bundle);
     method public void onLoadItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>);
+    method public void onSearch(java.lang.String, android.os.Bundle, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
     method public void setSessionToken(android.media.session.MediaSession.Token);
     field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
   }
@@ -38761,7 +38764,7 @@
     method public java.lang.CharSequence subSequence(int, int);
   }
 
-  public class AndroidCharacter {
+  public deprecated class AndroidCharacter {
     ctor public AndroidCharacter();
     method public static void getDirectionalities(char[], byte[], int);
     method public static int getEastAsianWidth(char);
@@ -39977,12 +39980,6 @@
     method public void writeToParcel(android.os.Parcel, int);
   }
 
-  public class RasterizerSpan extends android.text.style.CharacterStyle implements android.text.style.UpdateAppearance {
-    ctor public RasterizerSpan(android.graphics.Rasterizer);
-    method public android.graphics.Rasterizer getRasterizer();
-    method public void updateDrawState(android.text.TextPaint);
-  }
-
   public class RelativeSizeSpan extends android.text.style.MetricAffectingSpan implements android.text.ParcelableSpan {
     ctor public RelativeSizeSpan(float);
     ctor public RelativeSizeSpan(android.os.Parcel);
@@ -43076,11 +43073,13 @@
     method public final int getMeasuredWidthAndState();
     method public int getMinimumHeight();
     method public int getMinimumWidth();
+    method public int getNextClusterForwardId();
     method public int getNextFocusDownId();
     method public int getNextFocusForwardId();
     method public int getNextFocusLeftId();
     method public int getNextFocusRightId();
     method public int getNextFocusUpId();
+    method public int getNextSectionForwardId();
     method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
     method public android.view.ViewOutlineProvider getOutlineProvider();
     method public int getOverScrollMode();
@@ -43183,6 +43182,8 @@
     method public boolean isInEditMode();
     method public boolean isInLayout();
     method public boolean isInTouchMode();
+    method public final boolean isKeyboardNavigationCluster();
+    method public final boolean isKeyboardNavigationSection();
     method public boolean isLaidOut();
     method public boolean isLayoutDirectionResolved();
     method public boolean isLayoutRequested();
@@ -43351,6 +43352,8 @@
     method public void setId(int);
     method public void setImportantForAccessibility(int);
     method public void setKeepScreenOn(boolean);
+    method public void setKeyboardNavigationCluster(boolean);
+    method public void setKeyboardNavigationSection(boolean);
     method public void setLabelFor(int);
     method public void setLayerPaint(android.graphics.Paint);
     method public void setLayerType(int, android.graphics.Paint);
@@ -43362,11 +43365,13 @@
     method public void setMinimumHeight(int);
     method public void setMinimumWidth(int);
     method public void setNestedScrollingEnabled(boolean);
+    method public void setNextClusterForwardId(int);
     method public void setNextFocusDownId(int);
     method public void setNextFocusForwardId(int);
     method public void setNextFocusLeftId(int);
     method public void setNextFocusRightId(int);
     method public void setNextFocusUpId(int);
+    method public void setNextSectionForwardId(int);
     method public void setOnApplyWindowInsetsListener(android.view.View.OnApplyWindowInsetsListener);
     method public void setOnClickListener(android.view.View.OnClickListener);
     method public void setOnContextClickListener(android.view.View.OnContextClickListener);
diff --git a/api/removed.txt b/api/removed.txt
index 683a695..10e6eb5 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -78,10 +78,25 @@
     enum_constant public static final android.graphics.AvoidXfermode.Mode TARGET;
   }
 
+  public deprecated class LayerRasterizer extends android.graphics.Rasterizer {
+    ctor public LayerRasterizer();
+    method public void addLayer(android.graphics.Paint, float, float);
+    method public void addLayer(android.graphics.Paint);
+  }
+
+  public class Paint {
+    method public deprecated android.graphics.Rasterizer getRasterizer();
+    method public deprecated android.graphics.Rasterizer setRasterizer(android.graphics.Rasterizer);
+  }
+
   public deprecated class PixelXorXfermode extends android.graphics.Xfermode {
     ctor public PixelXorXfermode(int);
   }
 
+  public class Rasterizer {
+    ctor public Rasterizer();
+  }
+
 }
 
 package android.location {
@@ -393,6 +408,15 @@
 
 }
 
+package android.text.style {
+
+  public class RasterizerSpan extends android.text.style.CharacterStyle implements android.text.style.UpdateAppearance {
+    ctor public RasterizerSpan(android.graphics.Rasterizer);
+    method public android.graphics.Rasterizer getRasterizer();
+  }
+
+}
+
 package android.util {
 
   public deprecated class FloatMath {
diff --git a/api/system-current.txt b/api/system-current.txt
index 1ced79c..f8caa5f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -857,6 +857,8 @@
     field public static final int keyWidth = 16843325; // 0x101023d
     field public static final int keyboardLayout = 16843691; // 0x10103ab
     field public static final int keyboardMode = 16843341; // 0x101024d
+    field public static final int keyboardNavigationCluster = 16844096; // 0x1010540
+    field public static final int keyboardNavigationSection = 16844097; // 0x1010541
     field public static final int keycode = 16842949; // 0x10100c5
     field public static final int killAfterRestore = 16843420; // 0x101029c
     field public static final int label = 16842753; // 0x1010001
@@ -1000,11 +1002,13 @@
     field public static final int negativeButtonText = 16843254; // 0x10101f6
     field public static final int nestedScrollingEnabled = 16843830; // 0x1010436
     field public static final int networkSecurityConfig = 16844071; // 0x1010527
+    field public static final int nextClusterForward = 16844098; // 0x1010542
     field public static final int nextFocusDown = 16842980; // 0x10100e4
     field public static final int nextFocusForward = 16843580; // 0x101033c
     field public static final int nextFocusLeft = 16842977; // 0x10100e1
     field public static final int nextFocusRight = 16842978; // 0x10100e2
     field public static final int nextFocusUp = 16842979; // 0x10100e3
+    field public static final int nextSectionForward = 16844099; // 0x1010543
     field public static final int noHistory = 16843309; // 0x101022d
     field public static final int normalScreens = 16843397; // 0x1010285
     field public static final int notificationTimeout = 16843651; // 0x1010383
@@ -6280,6 +6284,8 @@
     method public boolean isCallerApplicationRestrictionsManagingPackage();
     method public boolean isDeviceManaged();
     method public boolean isDeviceOwnerApp(java.lang.String);
+    method public boolean isDeviceProvisioned();
+    method public boolean isDeviceProvisioningConfigApplied();
     method public boolean isLockTaskPermitted(java.lang.String);
     method public boolean isManagedProfile(android.content.ComponentName);
     method public boolean isMasterVolumeMuted(android.content.ComponentName);
@@ -6315,6 +6321,7 @@
     method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
     method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
     method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence);
+    method public void setDeviceProvisioningConfigApplied();
     method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
     method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
@@ -12274,6 +12281,7 @@
     enum_constant public static final android.graphics.Bitmap.Config ALPHA_8;
     enum_constant public static final deprecated android.graphics.Bitmap.Config ARGB_4444;
     enum_constant public static final android.graphics.Bitmap.Config ARGB_8888;
+    enum_constant public static final android.graphics.Bitmap.Config HARDWARE;
     enum_constant public static final android.graphics.Bitmap.Config RGB_565;
   }
 
@@ -12734,12 +12742,6 @@
     enum_constant public static final android.graphics.Interpolator.Result NORMAL;
   }
 
-  public deprecated class LayerRasterizer extends android.graphics.Rasterizer {
-    ctor public LayerRasterizer();
-    method public void addLayer(android.graphics.Paint, float, float);
-    method public void addLayer(android.graphics.Paint);
-  }
-
   public class LightingColorFilter extends android.graphics.ColorFilter {
     ctor public LightingColorFilter(int, int);
   }
@@ -12901,7 +12903,6 @@
     method public int getOffsetForAdvance(char[], int, int, int, int, boolean, float);
     method public int getOffsetForAdvance(java.lang.CharSequence, int, int, int, int, boolean, float);
     method public android.graphics.PathEffect getPathEffect();
-    method public deprecated android.graphics.Rasterizer getRasterizer();
     method public float getRunAdvance(char[], int, int, int, int, boolean, int);
     method public float getRunAdvance(java.lang.CharSequence, int, int, int, int, boolean, int);
     method public android.graphics.Shader getShader();
@@ -12958,7 +12959,6 @@
     method public void setLinearText(boolean);
     method public android.graphics.MaskFilter setMaskFilter(android.graphics.MaskFilter);
     method public android.graphics.PathEffect setPathEffect(android.graphics.PathEffect);
-    method public deprecated android.graphics.Rasterizer setRasterizer(android.graphics.Rasterizer);
     method public android.graphics.Shader setShader(android.graphics.Shader);
     method public void setShadowLayer(float, float, float, int);
     method public void setStrikeThruText(boolean);
@@ -13265,10 +13265,6 @@
     ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode);
   }
 
-  public deprecated class Rasterizer {
-    ctor public Rasterizer();
-  }
-
   public final class Rect implements android.os.Parcelable {
     ctor public Rect();
     ctor public Rect(int, int, int, int);
@@ -15777,7 +15773,6 @@
   }
 
   public final class GeofenceHardware {
-    ctor public GeofenceHardware(android.hardware.location.IGeofenceHardware);
     method public boolean addGeofence(int, int, android.hardware.location.GeofenceHardwareRequest, android.hardware.location.GeofenceHardwareCallback);
     method public int[] getMonitoringTypes();
     method public int getStatusOfMonitoringType(int);
@@ -15852,46 +15847,6 @@
     method public void setUnknownTimer(int);
   }
 
-  public final class GeofenceHardwareRequestParcelable implements android.os.Parcelable {
-    ctor public GeofenceHardwareRequestParcelable(int, android.hardware.location.GeofenceHardwareRequest);
-    method public int describeContents();
-    method public int getId();
-    method public int getLastTransition();
-    method public double getLatitude();
-    method public double getLongitude();
-    method public int getMonitorTransitions();
-    method public int getNotificationResponsiveness();
-    method public double getRadius();
-    method public int getUnknownTimer();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.hardware.location.GeofenceHardwareRequestParcelable> CREATOR;
-  }
-
-  public abstract interface IGeofenceHardware implements android.os.IInterface {
-    method public abstract boolean addCircularFence(int, android.hardware.location.GeofenceHardwareRequestParcelable, android.hardware.location.IGeofenceHardwareCallback) throws android.os.RemoteException;
-    method public abstract int[] getMonitoringTypes() throws android.os.RemoteException;
-    method public abstract int getStatusOfMonitoringType(int) throws android.os.RemoteException;
-    method public abstract boolean pauseGeofence(int, int) throws android.os.RemoteException;
-    method public abstract boolean registerForMonitorStateChangeCallback(int, android.hardware.location.IGeofenceHardwareMonitorCallback) throws android.os.RemoteException;
-    method public abstract boolean removeGeofence(int, int) throws android.os.RemoteException;
-    method public abstract boolean resumeGeofence(int, int, int) throws android.os.RemoteException;
-    method public abstract void setFusedGeofenceHardware(android.location.IFusedGeofenceHardware) throws android.os.RemoteException;
-    method public abstract void setGpsGeofenceHardware(android.location.IGpsGeofenceHardware) throws android.os.RemoteException;
-    method public abstract boolean unregisterForMonitorStateChangeCallback(int, android.hardware.location.IGeofenceHardwareMonitorCallback) throws android.os.RemoteException;
-  }
-
-  public abstract interface IGeofenceHardwareCallback implements android.os.IInterface {
-    method public abstract void onGeofenceAdd(int, int) throws android.os.RemoteException;
-    method public abstract void onGeofencePause(int, int) throws android.os.RemoteException;
-    method public abstract void onGeofenceRemove(int, int) throws android.os.RemoteException;
-    method public abstract void onGeofenceResume(int, int) throws android.os.RemoteException;
-    method public abstract void onGeofenceTransition(int, int, android.location.Location, long, int) throws android.os.RemoteException;
-  }
-
-  public abstract interface IGeofenceHardwareMonitorCallback implements android.os.IInterface {
-    method public abstract void onMonitoringSystemChange(android.hardware.location.GeofenceHardwareMonitorEvent) throws android.os.RemoteException;
-  }
-
   public class MemoryRegion implements android.os.Parcelable {
     ctor public MemoryRegion(android.os.Parcel);
     method public int describeContents();
@@ -21140,23 +21095,6 @@
     method public abstract void onNmeaReceived(long, java.lang.String);
   }
 
-  public abstract interface IFusedGeofenceHardware implements android.os.IInterface {
-    method public abstract void addGeofences(android.hardware.location.GeofenceHardwareRequestParcelable[]) throws android.os.RemoteException;
-    method public abstract boolean isSupported() throws android.os.RemoteException;
-    method public abstract void modifyGeofenceOptions(int, int, int, int, int, int) throws android.os.RemoteException;
-    method public abstract void pauseMonitoringGeofence(int) throws android.os.RemoteException;
-    method public abstract void removeGeofences(int[]) throws android.os.RemoteException;
-    method public abstract void resumeMonitoringGeofence(int, int) throws android.os.RemoteException;
-  }
-
-  public abstract interface IGpsGeofenceHardware implements android.os.IInterface {
-    method public abstract boolean addCircularHardwareGeofence(int, double, double, double, int, int, int, int) throws android.os.RemoteException;
-    method public abstract boolean isHardwareGeofenceSupported() throws android.os.RemoteException;
-    method public abstract boolean pauseHardwareGeofence(int) throws android.os.RemoteException;
-    method public abstract boolean removeHardwareGeofence(int) throws android.os.RemoteException;
-    method public abstract boolean resumeHardwareGeofence(int, int) throws android.os.RemoteException;
-  }
-
   public class Location implements android.os.Parcelable {
     ctor public Location(java.lang.String);
     ctor public Location(android.location.Location);
@@ -24259,6 +24197,7 @@
     method public android.content.ComponentName getServiceComponent();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public boolean isConnected();
+    method public void search(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SearchCallback);
     method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void unsubscribe(java.lang.String);
@@ -24294,6 +24233,12 @@
     field public static final int FLAG_PLAYABLE = 2; // 0x2
   }
 
+  public static abstract class MediaBrowser.SearchCallback {
+    ctor public MediaBrowser.SearchCallback();
+    method public void onError(java.lang.String, android.os.Bundle);
+    method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
+  }
+
   public static abstract class MediaBrowser.SubscriptionCallback {
     ctor public MediaBrowser.SubscriptionCallback();
     method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
@@ -25999,6 +25944,7 @@
     ctor public NetworkRecommendationProvider(android.os.Handler);
     method public final android.os.IBinder getBinder();
     method public abstract void onRequestRecommendation(android.net.RecommendationRequest, android.net.NetworkRecommendationProvider.ResultCallback);
+    method public abstract void onRequestScores(android.net.NetworkKey[]);
     field public static final java.lang.String EXTRA_RECOMMENDATION_RESULT = "android.net.extra.RECOMMENDATION_RESULT";
     field public static final java.lang.String EXTRA_SEQUENCE = "android.net.extra.SEQUENCE";
   }
@@ -32373,6 +32319,7 @@
     method public deprecated void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
     method public static boolean supportsMultipleUsers();
     field public static final java.lang.String ALLOW_PARENT_PROFILE_APP_LINKING = "allow_parent_profile_app_linking";
+    field public static final java.lang.String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
     field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
     field public static final java.lang.String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
     field public static final java.lang.String DISALLOW_APPS_CONTROL = "no_control_apps";
@@ -32398,6 +32345,7 @@
     field public static final java.lang.String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
     field public static final java.lang.String DISALLOW_OUTGOING_BEAM = "no_outgoing_beam";
     field public static final java.lang.String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls";
+    field public static final java.lang.String DISALLOW_REMOVE_MANAGED_PROFILE = "no_remove_managed_profile";
     field public static final java.lang.String DISALLOW_REMOVE_USER = "no_remove_user";
     field public static final java.lang.String DISALLOW_SAFE_BOOT = "no_safe_boot";
     field public static final java.lang.String DISALLOW_SET_USER_ICON = "no_set_user_icon";
@@ -37920,6 +37868,7 @@
     method public abstract void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
     method public void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>, android.os.Bundle);
     method public void onLoadItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>);
+    method public void onSearch(java.lang.String, android.os.Bundle, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
     method public void setSessionToken(android.media.session.MediaSession.Token);
     field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
   }
@@ -41954,7 +41903,7 @@
     method public java.lang.CharSequence subSequence(int, int);
   }
 
-  public class AndroidCharacter {
+  public deprecated class AndroidCharacter {
     ctor public AndroidCharacter();
     method public static void getDirectionalities(char[], byte[], int);
     method public static int getEastAsianWidth(char);
@@ -43170,12 +43119,6 @@
     method public void writeToParcel(android.os.Parcel, int);
   }
 
-  public class RasterizerSpan extends android.text.style.CharacterStyle implements android.text.style.UpdateAppearance {
-    ctor public RasterizerSpan(android.graphics.Rasterizer);
-    method public android.graphics.Rasterizer getRasterizer();
-    method public void updateDrawState(android.text.TextPaint);
-  }
-
   public class RelativeSizeSpan extends android.text.style.MetricAffectingSpan implements android.text.ParcelableSpan {
     ctor public RelativeSizeSpan(float);
     ctor public RelativeSizeSpan(android.os.Parcel);
@@ -46269,11 +46212,13 @@
     method public final int getMeasuredWidthAndState();
     method public int getMinimumHeight();
     method public int getMinimumWidth();
+    method public int getNextClusterForwardId();
     method public int getNextFocusDownId();
     method public int getNextFocusForwardId();
     method public int getNextFocusLeftId();
     method public int getNextFocusRightId();
     method public int getNextFocusUpId();
+    method public int getNextSectionForwardId();
     method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
     method public android.view.ViewOutlineProvider getOutlineProvider();
     method public int getOverScrollMode();
@@ -46376,6 +46321,8 @@
     method public boolean isInEditMode();
     method public boolean isInLayout();
     method public boolean isInTouchMode();
+    method public final boolean isKeyboardNavigationCluster();
+    method public final boolean isKeyboardNavigationSection();
     method public boolean isLaidOut();
     method public boolean isLayoutDirectionResolved();
     method public boolean isLayoutRequested();
@@ -46544,6 +46491,8 @@
     method public void setId(int);
     method public void setImportantForAccessibility(int);
     method public void setKeepScreenOn(boolean);
+    method public void setKeyboardNavigationCluster(boolean);
+    method public void setKeyboardNavigationSection(boolean);
     method public void setLabelFor(int);
     method public void setLayerPaint(android.graphics.Paint);
     method public void setLayerType(int, android.graphics.Paint);
@@ -46555,11 +46504,13 @@
     method public void setMinimumHeight(int);
     method public void setMinimumWidth(int);
     method public void setNestedScrollingEnabled(boolean);
+    method public void setNextClusterForwardId(int);
     method public void setNextFocusDownId(int);
     method public void setNextFocusForwardId(int);
     method public void setNextFocusLeftId(int);
     method public void setNextFocusRightId(int);
     method public void setNextFocusUpId(int);
+    method public void setNextSectionForwardId(int);
     method public void setOnApplyWindowInsetsListener(android.view.View.OnApplyWindowInsetsListener);
     method public void setOnClickListener(android.view.View.OnClickListener);
     method public void setOnContextClickListener(android.view.View.OnContextClickListener);
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 98e7953..0919b8f 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -76,10 +76,25 @@
     enum_constant public static final android.graphics.AvoidXfermode.Mode TARGET;
   }
 
+  public deprecated class LayerRasterizer extends android.graphics.Rasterizer {
+    ctor public LayerRasterizer();
+    method public void addLayer(android.graphics.Paint, float, float);
+    method public void addLayer(android.graphics.Paint);
+  }
+
+  public class Paint {
+    method public deprecated android.graphics.Rasterizer getRasterizer();
+    method public deprecated android.graphics.Rasterizer setRasterizer(android.graphics.Rasterizer);
+  }
+
   public deprecated class PixelXorXfermode extends android.graphics.Xfermode {
     ctor public PixelXorXfermode(int);
   }
 
+  public class Rasterizer {
+    ctor public Rasterizer();
+  }
+
 }
 
 package android.location {
@@ -387,6 +402,15 @@
 
 }
 
+package android.text.style {
+
+  public class RasterizerSpan extends android.text.style.CharacterStyle implements android.text.style.UpdateAppearance {
+    ctor public RasterizerSpan(android.graphics.Rasterizer);
+    method public android.graphics.Rasterizer getRasterizer();
+  }
+
+}
+
 package android.util {
 
   public deprecated class FloatMath {
diff --git a/api/test-current.txt b/api/test-current.txt
index 49f48d8..1b081e1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -750,6 +750,8 @@
     field public static final int keyWidth = 16843325; // 0x101023d
     field public static final int keyboardLayout = 16843691; // 0x10103ab
     field public static final int keyboardMode = 16843341; // 0x101024d
+    field public static final int keyboardNavigationCluster = 16844096; // 0x1010540
+    field public static final int keyboardNavigationSection = 16844097; // 0x1010541
     field public static final int keycode = 16842949; // 0x10100c5
     field public static final int killAfterRestore = 16843420; // 0x101029c
     field public static final int label = 16842753; // 0x1010001
@@ -893,11 +895,13 @@
     field public static final int negativeButtonText = 16843254; // 0x10101f6
     field public static final int nestedScrollingEnabled = 16843830; // 0x1010436
     field public static final int networkSecurityConfig = 16844071; // 0x1010527
+    field public static final int nextClusterForward = 16844098; // 0x1010542
     field public static final int nextFocusDown = 16842980; // 0x10100e4
     field public static final int nextFocusForward = 16843580; // 0x101033c
     field public static final int nextFocusLeft = 16842977; // 0x10100e1
     field public static final int nextFocusRight = 16842978; // 0x10100e2
     field public static final int nextFocusUp = 16842979; // 0x10100e3
+    field public static final int nextSectionForward = 16844099; // 0x1010543
     field public static final int noHistory = 16843309; // 0x101022d
     field public static final int normalScreens = 16843397; // 0x1010285
     field public static final int notificationTimeout = 16843651; // 0x1010383
@@ -11818,6 +11822,7 @@
     enum_constant public static final android.graphics.Bitmap.Config ALPHA_8;
     enum_constant public static final deprecated android.graphics.Bitmap.Config ARGB_4444;
     enum_constant public static final android.graphics.Bitmap.Config ARGB_8888;
+    enum_constant public static final android.graphics.Bitmap.Config HARDWARE;
     enum_constant public static final android.graphics.Bitmap.Config RGB_565;
   }
 
@@ -12278,12 +12283,6 @@
     enum_constant public static final android.graphics.Interpolator.Result NORMAL;
   }
 
-  public deprecated class LayerRasterizer extends android.graphics.Rasterizer {
-    ctor public LayerRasterizer();
-    method public void addLayer(android.graphics.Paint, float, float);
-    method public void addLayer(android.graphics.Paint);
-  }
-
   public class LightingColorFilter extends android.graphics.ColorFilter {
     ctor public LightingColorFilter(int, int);
   }
@@ -12445,7 +12444,6 @@
     method public int getOffsetForAdvance(char[], int, int, int, int, boolean, float);
     method public int getOffsetForAdvance(java.lang.CharSequence, int, int, int, int, boolean, float);
     method public android.graphics.PathEffect getPathEffect();
-    method public deprecated android.graphics.Rasterizer getRasterizer();
     method public float getRunAdvance(char[], int, int, int, int, boolean, int);
     method public float getRunAdvance(java.lang.CharSequence, int, int, int, int, boolean, int);
     method public android.graphics.Shader getShader();
@@ -12502,7 +12500,6 @@
     method public void setLinearText(boolean);
     method public android.graphics.MaskFilter setMaskFilter(android.graphics.MaskFilter);
     method public android.graphics.PathEffect setPathEffect(android.graphics.PathEffect);
-    method public deprecated android.graphics.Rasterizer setRasterizer(android.graphics.Rasterizer);
     method public android.graphics.Shader setShader(android.graphics.Shader);
     method public void setShadowLayer(float, float, float, int);
     method public void setStrikeThruText(boolean);
@@ -12809,10 +12806,6 @@
     ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode);
   }
 
-  public deprecated class Rasterizer {
-    ctor public Rasterizer();
-  }
-
   public final class Rect implements android.os.Parcelable {
     ctor public Rect();
     ctor public Rect(int, int, int, int);
@@ -22703,6 +22696,7 @@
     method public android.content.ComponentName getServiceComponent();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public boolean isConnected();
+    method public void search(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SearchCallback);
     method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void unsubscribe(java.lang.String);
@@ -22738,6 +22732,12 @@
     field public static final int FLAG_PLAYABLE = 2; // 0x2
   }
 
+  public static abstract class MediaBrowser.SearchCallback {
+    ctor public MediaBrowser.SearchCallback();
+    method public void onError(java.lang.String, android.os.Bundle);
+    method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
+  }
+
   public static abstract class MediaBrowser.SubscriptionCallback {
     ctor public MediaBrowser.SubscriptionCallback();
     method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
@@ -29810,6 +29810,7 @@
     method public deprecated void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
     method public static boolean supportsMultipleUsers();
     field public static final java.lang.String ALLOW_PARENT_PROFILE_APP_LINKING = "allow_parent_profile_app_linking";
+    field public static final java.lang.String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
     field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
     field public static final java.lang.String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
     field public static final java.lang.String DISALLOW_APPS_CONTROL = "no_control_apps";
@@ -29834,6 +29835,7 @@
     field public static final java.lang.String DISALLOW_NETWORK_RESET = "no_network_reset";
     field public static final java.lang.String DISALLOW_OUTGOING_BEAM = "no_outgoing_beam";
     field public static final java.lang.String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls";
+    field public static final java.lang.String DISALLOW_REMOVE_MANAGED_PROFILE = "no_remove_managed_profile";
     field public static final java.lang.String DISALLOW_REMOVE_USER = "no_remove_user";
     field public static final java.lang.String DISALLOW_SAFE_BOOT = "no_safe_boot";
     field public static final java.lang.String DISALLOW_SET_USER_ICON = "no_set_user_icon";
@@ -35179,6 +35181,7 @@
     method public abstract void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
     method public void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>, android.os.Bundle);
     method public void onLoadItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>);
+    method public void onSearch(java.lang.String, android.os.Bundle, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
     method public void setSessionToken(android.media.session.MediaSession.Token);
     field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
   }
@@ -38860,7 +38863,7 @@
     method public java.lang.CharSequence subSequence(int, int);
   }
 
-  public class AndroidCharacter {
+  public deprecated class AndroidCharacter {
     ctor public AndroidCharacter();
     method public static void getDirectionalities(char[], byte[], int);
     method public static int getEastAsianWidth(char);
@@ -40076,12 +40079,6 @@
     method public void writeToParcel(android.os.Parcel, int);
   }
 
-  public class RasterizerSpan extends android.text.style.CharacterStyle implements android.text.style.UpdateAppearance {
-    ctor public RasterizerSpan(android.graphics.Rasterizer);
-    method public android.graphics.Rasterizer getRasterizer();
-    method public void updateDrawState(android.text.TextPaint);
-  }
-
   public class RelativeSizeSpan extends android.text.style.MetricAffectingSpan implements android.text.ParcelableSpan {
     ctor public RelativeSizeSpan(float);
     ctor public RelativeSizeSpan(android.os.Parcel);
@@ -43344,11 +43341,13 @@
     method public final int getMeasuredWidthAndState();
     method public int getMinimumHeight();
     method public int getMinimumWidth();
+    method public int getNextClusterForwardId();
     method public int getNextFocusDownId();
     method public int getNextFocusForwardId();
     method public int getNextFocusLeftId();
     method public int getNextFocusRightId();
     method public int getNextFocusUpId();
+    method public int getNextSectionForwardId();
     method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
     method public android.view.ViewOutlineProvider getOutlineProvider();
     method public int getOverScrollMode();
@@ -43452,6 +43451,8 @@
     method public boolean isInEditMode();
     method public boolean isInLayout();
     method public boolean isInTouchMode();
+    method public final boolean isKeyboardNavigationCluster();
+    method public final boolean isKeyboardNavigationSection();
     method public boolean isLaidOut();
     method public boolean isLayoutDirectionResolved();
     method public boolean isLayoutRequested();
@@ -43620,6 +43621,8 @@
     method public void setId(int);
     method public void setImportantForAccessibility(int);
     method public void setKeepScreenOn(boolean);
+    method public void setKeyboardNavigationCluster(boolean);
+    method public void setKeyboardNavigationSection(boolean);
     method public void setLabelFor(int);
     method public void setLayerPaint(android.graphics.Paint);
     method public void setLayerType(int, android.graphics.Paint);
@@ -43631,11 +43634,13 @@
     method public void setMinimumHeight(int);
     method public void setMinimumWidth(int);
     method public void setNestedScrollingEnabled(boolean);
+    method public void setNextClusterForwardId(int);
     method public void setNextFocusDownId(int);
     method public void setNextFocusForwardId(int);
     method public void setNextFocusLeftId(int);
     method public void setNextFocusRightId(int);
     method public void setNextFocusUpId(int);
+    method public void setNextSectionForwardId(int);
     method public void setOnApplyWindowInsetsListener(android.view.View.OnApplyWindowInsetsListener);
     method public void setOnClickListener(android.view.View.OnClickListener);
     method public void setOnContextClickListener(android.view.View.OnContextClickListener);
diff --git a/api/test-removed.txt b/api/test-removed.txt
index 683a695..10e6eb5 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -78,10 +78,25 @@
     enum_constant public static final android.graphics.AvoidXfermode.Mode TARGET;
   }
 
+  public deprecated class LayerRasterizer extends android.graphics.Rasterizer {
+    ctor public LayerRasterizer();
+    method public void addLayer(android.graphics.Paint, float, float);
+    method public void addLayer(android.graphics.Paint);
+  }
+
+  public class Paint {
+    method public deprecated android.graphics.Rasterizer getRasterizer();
+    method public deprecated android.graphics.Rasterizer setRasterizer(android.graphics.Rasterizer);
+  }
+
   public deprecated class PixelXorXfermode extends android.graphics.Xfermode {
     ctor public PixelXorXfermode(int);
   }
 
+  public class Rasterizer {
+    ctor public Rasterizer();
+  }
+
 }
 
 package android.location {
@@ -393,6 +408,15 @@
 
 }
 
+package android.text.style {
+
+  public class RasterizerSpan extends android.text.style.CharacterStyle implements android.text.style.UpdateAppearance {
+    ctor public RasterizerSpan(android.graphics.Rasterizer);
+    method public android.graphics.Rasterizer getRasterizer();
+  }
+
+}
+
 package android.util {
 
   public deprecated class FloatMath {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f6f5472..f04455b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1002,6 +1002,16 @@
     }
 
     /**
+     * Returns true if the system supports split screen multi-window.
+     * @hide
+     */
+    static public boolean supportsSplitScreenMultiWindow() {
+        return supportsMultiWindow()
+                && Resources.getSystem().getBoolean(
+                    com.android.internal.R.bool.config_supportsSplitScreenMultiWindow);
+    }
+
+    /**
      * Information you can set and retrieve about the current activity within the recent task list.
      */
     public static class TaskDescription implements Parcelable {
diff --git a/core/java/android/app/QueuedWork.java b/core/java/android/app/QueuedWork.java
index 6ee4780..0ae8505 100644
--- a/core/java/android/app/QueuedWork.java
+++ b/core/java/android/app/QueuedWork.java
@@ -16,86 +16,197 @@
 
 package android.app;
 
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.LinkedList;
 
 /**
- * Internal utility class to keep track of process-global work that's
- * outstanding and hasn't been finished yet.
+ * Internal utility class to keep track of process-global work that's outstanding and hasn't been
+ * finished yet.
  *
- * This was created for writing SharedPreference edits out
- * asynchronously so we'd have a mechanism to wait for the writes in
- * Activity.onPause and similar places, but we may use this mechanism
- * for other things in the future.
+ * New work will be {@link #queue queued}.
+ *
+ * It is possible to add 'finisher'-runnables that are {@link #waitToFinish guaranteed to be run}.
+ * This is used to make sure the work has been finished.
+ *
+ * This was created for writing SharedPreference edits out asynchronously so we'd have a mechanism
+ * to wait for the writes in Activity.onPause and similar places, but we may use this mechanism for
+ * other things in the future.
+ *
+ * The queued asynchronous work is performed on a separate, dedicated thread.
  *
  * @hide
  */
 public class QueuedWork {
+    private static final String LOG_TAG = QueuedWork.class.getSimpleName();
 
-    // The set of Runnables that will finish or wait on any async
-    // activities started by the application.
-    private static final ConcurrentLinkedQueue<Runnable> sPendingWorkFinishers =
-            new ConcurrentLinkedQueue<Runnable>();
+    /** Delay for delayed runnables */
+    private static final long DELAY = 50;
 
-    private static ExecutorService sSingleThreadExecutor = null; // lazy, guarded by class
+    /** Lock for this class */
+    private static final Object sLock = new Object();
+
+    /** Finishers {@link #addFinisher added} and not yet {@link #removeFinisher removed} */
+    @GuardedBy("sLock")
+    private static final LinkedList<Runnable> sFinishers = new LinkedList<>();
+
+    /** {@link #getHandler() Lazily} created handler */
+    @GuardedBy("sLock")
+    private static Handler sHandler = null;
+
+    /** Work queued via {@link #queue} */
+    @GuardedBy("sLock")
+    private static final LinkedList<Runnable> sWork = new LinkedList<>();
+
+    /** If new work can be delayed or not */
+    @GuardedBy("sLock")
+    private static boolean sCanDelay = true;
 
     /**
-     * Returns a single-thread Executor shared by the entire process,
-     * creating it if necessary.
+     * Lazily create a handler on a separate thread.
+     *
+     * @return the handler
      */
-    public static ExecutorService singleThreadExecutor() {
-        synchronized (QueuedWork.class) {
-            if (sSingleThreadExecutor == null) {
-                // TODO: can we give this single thread a thread name?
-                sSingleThreadExecutor = Executors.newSingleThreadExecutor();
+    private static Handler getHandler() {
+        synchronized (sLock) {
+            if (sHandler == null) {
+                HandlerThread handlerThread = new HandlerThread("queued-work-looper",
+                        Process.THREAD_PRIORITY_BACKGROUND);
+                handlerThread.start();
+
+                sHandler = new QueuedWorkHandler(handlerThread.getLooper());
             }
-            return sSingleThreadExecutor;
+            return sHandler;
         }
     }
 
     /**
-     * Add a runnable to finish (or wait for) a deferred operation
-     * started in this context earlier.  Typically finished by e.g.
-     * an Activity#onPause.  Used by SharedPreferences$Editor#startCommit().
+     * Add a finisher-runnable to wait for {@link #queue asynchronously processed work}.
      *
-     * Note that this doesn't actually start it running.  This is just
-     * a scratch set for callers doing async work to keep updated with
-     * what's in-flight.  In the common case, caller code
-     * (e.g. SharedPreferences) will pretty quickly call remove()
-     * after an add().  The only time these Runnables are run is from
-     * waitToFinish(), below.
+     * Used by SharedPreferences$Editor#startCommit().
+     *
+     * Note that this doesn't actually start it running.  This is just a scratch set for callers
+     * doing async work to keep updated with what's in-flight. In the common case, caller code
+     * (e.g. SharedPreferences) will pretty quickly call remove() after an add(). The only time
+     * these Runnables are run is from {@link #waitToFinish}.
+     *
+     * @param finisher The runnable to add as finisher
      */
-    public static void add(Runnable finisher) {
-        sPendingWorkFinishers.add(finisher);
-    }
-
-    public static void remove(Runnable finisher) {
-        sPendingWorkFinishers.remove(finisher);
+    public static void addFinisher(Runnable finisher) {
+        synchronized (sLock) {
+            sFinishers.add(finisher);
+        }
     }
 
     /**
-     * Finishes or waits for async operations to complete.
-     * (e.g. SharedPreferences$Editor#startCommit writes)
+     * Remove a previously {@link #addFinisher added} finisher-runnable.
      *
-     * Is called from the Activity base class's onPause(), after
-     * BroadcastReceiver's onReceive, after Service command handling,
-     * etc.  (so async work is never lost)
+     * @param finisher The runnable to remove.
+     */
+    public static void removeFinisher(Runnable finisher) {
+        synchronized (sLock) {
+            sFinishers.remove(finisher);
+        }
+    }
+
+    /**
+     * Trigger queued work to be processed immediately. The queued work is processed on a separate
+     * thread asynchronous. While doing that run and process all finishers on this thread. The
+     * finishers can be implemented in a way to check weather the queued work is finished.
+     *
+     * Is called from the Activity base class's onPause(), after BroadcastReceiver's onReceive,
+     * after Service command handling, etc. (so async work is never lost)
      */
     public static void waitToFinish() {
-        Runnable toFinish;
-        while ((toFinish = sPendingWorkFinishers.poll()) != null) {
-            toFinish.run();
+        Handler handler = getHandler();
+
+        synchronized (sLock) {
+            if (handler.hasMessages(QueuedWorkHandler.MSG_RUN)) {
+                // Force the delayed work to be processed now
+                handler.removeMessages(QueuedWorkHandler.MSG_RUN);
+                handler.sendEmptyMessage(QueuedWorkHandler.MSG_RUN);
+            }
+
+            // We should not delay any work as this might delay the finishers
+            sCanDelay = false;
+        }
+
+        try {
+            while (true) {
+                Runnable finisher;
+
+                synchronized (sLock) {
+                    finisher = sFinishers.poll();
+                }
+
+                if (finisher == null) {
+                    break;
+                }
+
+                finisher.run();
+            }
+        } finally {
+            sCanDelay = true;
         }
     }
-    
+
     /**
-     * Returns true if there is pending work to be done.  Note that the
-     * result is out of data as soon as you receive it, so be careful how you
-     * use it.
+     * Queue a work-runnable for processing asynchronously.
+     *
+     * @param work The new runnable to process
+     * @param shouldDelay If the message should be delayed
+     */
+    public static void queue(Runnable work, boolean shouldDelay) {
+        Handler handler = getHandler();
+
+        synchronized (sLock) {
+            sWork.add(work);
+
+            if (shouldDelay && sCanDelay) {
+                handler.sendEmptyMessageDelayed(QueuedWorkHandler.MSG_RUN, DELAY);
+            } else {
+                handler.sendEmptyMessage(QueuedWorkHandler.MSG_RUN);
+            }
+        }
+    }
+
+    /**
+     * @return True iff there is any {@link #queue async work queued}.
      */
     public static boolean hasPendingWork() {
-        return !sPendingWorkFinishers.isEmpty();
+        synchronized (sLock) {
+            return !sWork.isEmpty();
+        }
     }
-    
+
+    private static class QueuedWorkHandler extends Handler {
+        static final int MSG_RUN = 1;
+
+        QueuedWorkHandler(Looper looper) {
+            super(looper);
+        }
+
+        public void handleMessage(Message msg) {
+            if (msg.what == MSG_RUN) {
+                LinkedList<Runnable> work;
+
+                synchronized (sWork) {
+                    work = (LinkedList<Runnable>) sWork.clone();
+                    sWork.clear();
+
+                    // Remove all msg-s as all work will be processed now
+                    removeMessages(MSG_RUN);
+                }
+
+                work.forEach(Runnable::run);
+            }
+        }
+    }
 }
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index f273cd8..5943433 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -379,12 +379,12 @@
                     }
                 };
 
-            QueuedWork.add(awaitCommit);
+            QueuedWork.addFinisher(awaitCommit);
 
             Runnable postWriteRunnable = new Runnable() {
                     public void run() {
                         awaitCommit.run();
-                        QueuedWork.remove(awaitCommit);
+                        QueuedWork.removeFinisher(awaitCommit);
                     }
                 };
 
@@ -557,10 +557,10 @@
         }
 
         if (DEBUG) {
-            Log.d(TAG, "added " + mcr.memoryStateGeneration + " -> " + mFile.getName());
+            Log.d(TAG, "queued " + mcr.memoryStateGeneration + " -> " + mFile.getName());
         }
 
-        QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);
+        QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
     }
 
     private static FileOutputStream createFileOutputStream(File file) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 39f415e..f77bed0 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1254,8 +1254,8 @@
     /**
      * Result code for {@link checkProvisioningPreCondition}.
      *
-     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_PROFILE} when the device has a device owner
-     * and the user is a system user on a split system user device.
+     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_PROFILE} when the device the user is a
+     * system user on a split system user device.
      *
      * @hide
      */
@@ -6827,10 +6827,13 @@
     }
 
     /**
+     * Returns whether the device has been provisioned.
+     *
+     * <p>Not for use by third-party applications.
+     *
      * @hide
-     * @return whether {@link android.provider.Settings.Global#DEVICE_PROVISIONED} has ever been set
-     * to 1.
      */
+    @SystemApi
     public boolean isDeviceProvisioned() {
         try {
             return mService.isDeviceProvisioned();
@@ -6840,9 +6843,16 @@
     }
 
     /**
-     * @hide
-     * Writes that the provisioning configuration has been applied.
-     */
+      * Writes that the provisioning configuration has been applied.
+      *
+      * <p>The caller must hold the {@link android.Manifest.permission#MANAGE_USERS}
+      * permission.
+      *
+      * <p>Not for use by third-party applications.
+      *
+      * @hide
+      */
+    @SystemApi
     public void setDeviceProvisioningConfigApplied() {
         try {
             mService.setDeviceProvisioningConfigApplied();
@@ -6852,9 +6862,17 @@
     }
 
     /**
-     * @hide
+     * Returns whether the provisioning configuration has been applied.
+     *
+     * <p>The caller must hold the {@link android.Manifest.permission#MANAGE_USERS} permission.
+     *
+     * <p>Not for use by third-party applications.
+     *
      * @return whether the provisioning configuration has been applied.
+     *
+     * @hide
      */
+    @SystemApi
     public boolean isDeviceProvisioningConfigApplied() {
         try {
             return mService.isDeviceProvisioningConfigApplied();
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 342c285..31b2359 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -34,4 +34,6 @@
     boolean isAppInactive(String packageName, int userId);
     void whitelistAppTemporarily(String packageName, long duration, int userId);
     void onCarrierPrivilegedAppsChanged();
+    void reportChooserSelection(String packageName, int userId, String contentType,
+            in String[] annotations, String action);
 }
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index a0da258..ce8b05a 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -86,6 +86,12 @@
         public static final int SHORTCUT_INVOCATION = 8;
 
         /**
+         * An event type denoting that a package was selected by the user for ChooserActivity.
+         * @hide
+         */
+        public static final int CHOOSER_ACTION = 9;
+
+        /**
          * {@hide}
          */
         public String mPackage;
@@ -119,6 +125,27 @@
         public String mShortcutId;
 
         /**
+         * Action type passed to ChooserActivity
+         * Only present for {@link #CHOOSER_ACTION} event types.
+         * {@hide}
+         */
+        public String mAction;
+
+        /**
+         * Content type passed to ChooserActivity.
+         * Only present for {@link #CHOOSER_ACTION} event types.
+         * {@hide}
+         */
+        public String mContentType;
+
+        /**
+         * Content annotations passed to ChooserActivity.
+         * Only present for {@link #CHOOSER_ACTION} event types.
+         * {@hide}
+         */
+        public String[] mContentAnnotations;
+
+        /**
          * The package name of the source of this event.
          */
         public String getPackageName() {
@@ -307,6 +334,11 @@
             case Event.SHORTCUT_INVOCATION:
                 p.writeString(event.mShortcutId);
                 break;
+            case Event.CHOOSER_ACTION:
+                p.writeString(event.mAction);
+                p.writeString(event.mContentType);
+                p.writeStringArray(event.mContentAnnotations);
+                break;
         }
     }
 
@@ -333,6 +365,9 @@
         // Fill out the event-dependant fields.
         eventOut.mConfiguration = null;
         eventOut.mShortcutId = null;
+        eventOut.mAction = null;
+        eventOut.mContentType = null;
+        eventOut.mContentAnnotations = null;
 
         switch (eventOut.mEventType) {
             case Event.CONFIGURATION_CHANGE:
@@ -342,6 +377,11 @@
             case Event.SHORTCUT_INVOCATION:
                 eventOut.mShortcutId = p.readString();
                 break;
+            case Event.CHOOSER_ACTION:
+                eventOut.mAction = p.readString();
+                eventOut.mContentType = p.readString();
+                eventOut.mContentAnnotations = p.createStringArray();
+                break;
         }
     }
 
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index 2937ccc..57f18f1 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -16,8 +16,10 @@
 
 package android.app.usage;
 
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.ArrayMap;
 
 /**
  * Contains usage statistics for an app package for a specific
@@ -64,6 +66,11 @@
     /**
      * {@hide}
      */
+    public ArrayMap<String, ArrayMap<String, Integer>> mChooserCounts;
+
+    /**
+     * {@hide}
+     */
     public UsageStats() {
     }
 
@@ -75,6 +82,7 @@
         mTotalTimeInForeground = stats.mTotalTimeInForeground;
         mLaunchCount = stats.mLaunchCount;
         mLastEvent = stats.mLastEvent;
+        mChooserCounts = stats.mChooserCounts;
     }
 
     public String getPackageName() {
@@ -142,6 +150,26 @@
         mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
         mTotalTimeInForeground += right.mTotalTimeInForeground;
         mLaunchCount += right.mLaunchCount;
+        if (mChooserCounts == null) {
+            mChooserCounts = right.mChooserCounts;
+        } else if (right.mChooserCounts != null) {
+            final int chooserCountsSize = right.mChooserCounts.size();
+            for (int i = 0; i < chooserCountsSize; i++) {
+                String action = right.mChooserCounts.keyAt(i);
+                ArrayMap<String, Integer> counts = right.mChooserCounts.valueAt(i);
+                if (!mChooserCounts.containsKey(action) || mChooserCounts.get(action) == null) {
+                    mChooserCounts.put(action, counts);
+                    continue;
+                }
+                final int annotationSize = counts.size();
+                for (int j = 0; j < annotationSize; j++) {
+                    String key = counts.keyAt(j);
+                    int rightValue = counts.valueAt(j);
+                    int leftValue = mChooserCounts.get(action).getOrDefault(key, 0);
+                    mChooserCounts.get(action).put(key, leftValue + rightValue);
+                }
+            }
+        }
     }
 
     @Override
@@ -158,6 +186,21 @@
         dest.writeLong(mTotalTimeInForeground);
         dest.writeInt(mLaunchCount);
         dest.writeInt(mLastEvent);
+        Bundle allCounts = new Bundle();
+        if (mChooserCounts != null) {
+            final int chooserCountSize = mChooserCounts.size();
+            for (int i = 0; i < chooserCountSize; i++) {
+                String action = mChooserCounts.keyAt(i);
+                ArrayMap<String, Integer> counts = mChooserCounts.valueAt(i);
+                Bundle currentCounts = new Bundle();
+                final int annotationSize = counts.size();
+                for (int j = 0; j < annotationSize; j++) {
+                    currentCounts.putInt(counts.keyAt(j), counts.valueAt(j));
+                }
+                allCounts.putBundle(action, currentCounts);
+            }
+        }
+        dest.writeBundle(allCounts);
     }
 
     public static final Creator<UsageStats> CREATOR = new Creator<UsageStats>() {
@@ -171,6 +214,25 @@
             stats.mTotalTimeInForeground = in.readLong();
             stats.mLaunchCount = in.readInt();
             stats.mLastEvent = in.readInt();
+            Bundle allCounts = in.readBundle();
+            if (allCounts != null) {
+                stats.mChooserCounts = new ArrayMap<>();
+                for (String action : allCounts.keySet()) {
+                    if (!stats.mChooserCounts.containsKey(action)) {
+                        ArrayMap<String, Integer> newCounts = new ArrayMap<>();
+                        stats.mChooserCounts.put(action, newCounts);
+                    }
+                    Bundle currentCounts = allCounts.getBundle(action);
+                    if (currentCounts != null) {
+                        for (String key : currentCounts.keySet()) {
+                            int value = currentCounts.getInt(key);
+                            if (value > 0) {
+                                stats.mChooserCounts.get(action).put(key, value);
+                            }
+                        }
+                    }
+                }
+            }
             return stats;
         }
 
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 2aeecfa..75a4a53 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -278,4 +278,23 @@
         } catch (RemoteException re) {
         }
     }
+
+    /**
+     * Reports a Chooser action to the UsageStatsManager.
+     *
+     * @param packageName The package name of the app that is selected.
+     * @param userId The user id of who makes the selection.
+     * @param contentType The type of the content, e.g., Image, Video, App.
+     * @param annotations The annotations of the content, e.g., Game, Selfie.
+     * @param action The action type of Intent that invokes ChooserActivity.
+     * {@link UsageEvents}
+     * @hide
+     */
+    public void reportChooserSelection(String packageName, int userId, String contentType,
+                                       String[] annotations, String action) {
+        try {
+            mService.reportChooserSelection(packageName, userId, contentType, annotations, action);
+        } catch (RemoteException re) {
+        }
+    }
 }
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index b0e27a4..6c1e2a9 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -201,6 +201,23 @@
     public static final int STATE_BLE_TURNING_OFF = 16;
 
     /**
+     * Human-readable string helper for AdapterState
+     * @hide
+     */
+    public static String nameForState(@AdapterState int state) {
+        switch(state) {
+            case STATE_OFF: return "OFF";
+            case STATE_TURNING_ON: return "TURNING_ON";
+            case STATE_ON: return "ON";
+            case STATE_TURNING_OFF: return "TURNING_OFF";
+            case STATE_BLE_TURNING_ON: return "BLE_TURNING_ON";
+            case STATE_BLE_ON: return "BLE_ON";
+            case STATE_BLE_TURNING_OFF: return "BLE_TURNING_OFF";
+            default: return "?!?!? (" + state + ")";
+        }
+    }
+
+    /**
      * Activity Action: Show a system activity that requests discoverable mode.
      * This activity will also request the user to turn on Bluetooth if it
      * is not currently enabled.
@@ -658,15 +675,8 @@
     @SystemApi
     public boolean isLeEnabled() {
        final int state = getLeState();
-       if (state == BluetoothAdapter.STATE_ON) {
-           if (DBG) Log.d (TAG, "STATE_ON");
-       } else if (state == BluetoothAdapter.STATE_BLE_ON) {
-           if (DBG) Log.d (TAG, "STATE_BLE_ON");
-       } else {
-           if (DBG) Log.d (TAG, "STATE_OFF");
-           return false;
-       }
-       return true;
+       if (DBG) Log.d(TAG, "isLeEnabled(): " + BluetoothAdapter.nameForState(state));
+       return (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON);
     }
 
     /**
@@ -831,10 +841,10 @@
         if (state == BluetoothAdapter.STATE_BLE_ON
             || state == BluetoothAdapter.STATE_BLE_TURNING_ON
             || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
-            if (VDBG) Log.d(TAG, "Consider internal state as OFF");
+            if (VDBG) Log.d(TAG, "Consider " + BluetoothAdapter.nameForState(state) + " state as OFF");
             state = BluetoothAdapter.STATE_OFF;
         }
-        if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state);
+        if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + BluetoothAdapter.nameForState(state));
         return state;
     }
 
@@ -871,12 +881,12 @@
             mServiceLock.readLock().unlock();
         }
 
-        if (VDBG) Log.d(TAG,"getLeState() returning " + state);
+        if (VDBG) Log.d(TAG,"getLeState() returning " + BluetoothAdapter.nameForState(state));
         return state;
     }
 
     boolean getLeAccess() {
-        if(getLeState() == STATE_ON)
+        if (getLeState() == STATE_ON)
             return true;
 
         else if (getLeState() == STATE_BLE_ON)
@@ -914,8 +924,8 @@
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean enable() {
-        if (isEnabled() == true) {
-            if (DBG) Log.d(TAG, "enable(): BT is already enabled..!");
+        if (isEnabled()) {
+            if (DBG) Log.d(TAG, "enable(): BT already enabled!");
             return true;
         }
         try {
@@ -1518,8 +1528,9 @@
                     }
                 }
             }
-        } catch (RemoteException e) {Log.e(TAG, "getSupportedProfiles:", e);}
-
+        } catch (RemoteException e) {
+          Log.e(TAG, "getSupportedProfiles:", e);
+        }
         return supportedProfiles;
     }
 
@@ -1870,34 +1881,6 @@
      * @hide
      */
     public Pair<byte[], byte[]> readOutOfBandData() {
-        if (getState() != STATE_ON) return null;
-        //TODO(BT
-        /*
-        try {
-            byte[] hash;
-            byte[] randomizer;
-
-            byte[] ret = null;
-            mServiceLock.readLock().lock();
-            if (mService != null) mService.readOutOfBandData();
-
-            if (ret  == null || ret.length != 32) return null;
-
-            hash = Arrays.copyOfRange(ret, 0, 16);
-            randomizer = Arrays.copyOfRange(ret, 16, 32);
-
-            if (DBG) {
-                Log.d(TAG, "readOutOfBandData:" + Arrays.toString(hash) +
-                  ":" + Arrays.toString(randomizer));
-            }
-            return new Pair<byte[], byte[]>(hash, randomizer);
-
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        */
         return null;
     }
 
@@ -2051,7 +2034,7 @@
                             if (cb != null) {
                                 cb.onBluetoothServiceUp(bluetoothService);
                             } else {
-                                Log.d(TAG, "onBluetoothServiceUp: cb is null!!!");
+                                Log.d(TAG, "onBluetoothServiceUp: cb is null!");
                             }
                         } catch (Exception e) {
                             Log.e(TAG,"",e);
@@ -2079,7 +2062,7 @@
                             if (cb != null) {
                                 cb.onBluetoothServiceDown();
                             } else {
-                                Log.d(TAG, "onBluetoothServiceDown: cb is null!!!");
+                                Log.d(TAG, "onBluetoothServiceDown: cb is null!");
                             }
                         } catch (Exception e) {
                             Log.e(TAG,"",e);
@@ -2089,7 +2072,7 @@
             }
 
             public void onBrEdrDown() {
-                if (DBG) Log.i(TAG, "onBrEdrDown:");
+                if (VDBG) Log.i(TAG, "onBrEdrDown: " + mService);
             }
     };
 
@@ -2100,7 +2083,7 @@
      */
     public boolean enableNoAutoConnect() {
         if (isEnabled() == true){
-            if (DBG) Log.d(TAG, "enableNoAutoConnect(): BT is already enabled..!");
+            if (DBG) Log.d(TAG, "enableNoAutoConnect(): BT already enabled!");
             return true;
         }
         try {
@@ -2140,22 +2123,6 @@
      */
     public boolean changeApplicationBluetoothState(boolean on,
                                                    BluetoothStateChangeCallback callback) {
-        if (callback == null) return false;
-
-        //TODO(BT)
-        /*
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.changeApplicationBluetoothState(on, new
-                    StateChangeCallbackWrapper(callback), new Binder());
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "changeBluetoothState", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        */
         return false;
     }
 
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index 231dace..c456288 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -375,16 +375,16 @@
                     // of the list to finish the broadcast, so we don't block this
                     // thread (which may be the main thread) to have it finished.
                     //
-                    // Note that we don't need to use QueuedWork.add() with the
+                    // Note that we don't need to use QueuedWork.addFinisher() with the
                     // runnable, since we know the AM is waiting for us until the
                     // executor gets to it.
-                    QueuedWork.singleThreadExecutor().execute( new Runnable() {
+                    QueuedWork.queue(new Runnable() {
                         @Override public void run() {
                             if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                                     "Finishing broadcast after work to component " + mToken);
                             sendFinished(mgr);
                         }
-                    });
+                    }, false);
                 } else {
                     if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                             "Finishing broadcast to component " + mToken);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 026ea9b..2fbb5b1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3489,15 +3489,15 @@
     public abstract @Nullable String[] getPackagesForUid(int uid);
 
     /**
-     * Retrieve the official name associated with a user id.  This name is
+     * Retrieve the official name associated with a uid. This name is
      * guaranteed to never change, though it is possible for the underlying
-     * user id to be changed.  That is, if you are storing information about
-     * user ids in persistent storage, you should use the string returned
-     * by this function instead of the raw user-id.
+     * uid to be changed.  That is, if you are storing information about
+     * uids in persistent storage, you should use the string returned
+     * by this function instead of the raw uid.
      *
-     * @param uid The user id for which you would like to retrieve a name.
-     * @return Returns a unique name for the given user id, or null if the
-     * user id is not currently assigned.
+     * @param uid The uid for which you would like to retrieve a name.
+     * @return Returns a unique name for the given uid, or null if the
+     * uid is not currently assigned.
      */
     public abstract @Nullable String getNameForUid(int uid);
 
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index ad0a6b2..1ba68a6 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -197,15 +197,15 @@
             boolean overridePolicy);
 
     /**
-     * Retrieve the official name associated with a user id.  This name is
+     * Retrieve the official name associated with a uid. This name is
      * guaranteed to never change, though it is possible for the underlying
-     * user id to be changed.  That is, if you are storing information about
-     * user ids in persistent storage, you should use the string returned
-     * by this function instead of the raw user-id.
+     * uid to be changed. That is, if you are storing information about
+     * uids in persistent storage, you should use the string returned
+     * by this function instead of the raw uid.
      *
-     * @param uid The user id for which you would like to retrieve a name.
-     * @return Returns a unique name for the given user id, or null if the
-     * user id is not currently assigned.
+     * @param uid The uid for which you would like to retrieve a name.
+     * @return Returns a unique name for the given uid, or null if the
+     * uid is not currently assigned.
      */
     public abstract String getNameForUid(int uid);
 
diff --git a/core/java/android/hardware/CameraStatus.java b/core/java/android/hardware/CameraStatus.java
new file mode 100644
index 0000000..bc97e38
--- /dev/null
+++ b/core/java/android/hardware/CameraStatus.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Status information about a camera.
+ *
+ * Contains the name of the camera device, and its current status, one of the
+ * ICameraServiceListener.STATUS_ values.
+ *
+ * @hide
+ */
+public class CameraStatus implements Parcelable {
+    public String cameraId;
+    public int status;
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(cameraId);
+        out.writeInt(status);
+    }
+
+    public void readFromParcel(Parcel in) {
+        cameraId = in.readString();
+        status = in.readInt();
+    }
+
+    public static final Parcelable.Creator<CameraStatus> CREATOR =
+            new Parcelable.Creator<CameraStatus>() {
+        @Override
+        public CameraStatus createFromParcel(Parcel in) {
+            CameraStatus status = new CameraStatus();
+            status.readFromParcel(in);
+
+            return status;
+        }
+
+        @Override
+        public CameraStatus[] newArray(int size) {
+            return new CameraStatus[size];
+        }
+    };
+};
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 145b1d0..e8e989f 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -23,6 +23,7 @@
 import android.hardware.ICameraService;
 import android.hardware.ICameraServiceListener;
 import android.hardware.CameraInfo;
+import android.hardware.CameraStatus;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.legacy.CameraDeviceUserShim;
 import android.hardware.camera2.legacy.LegacyMetadataMapper;
@@ -92,11 +93,7 @@
      */
     @NonNull
     public String[] getCameraIdList() throws CameraAccessException {
-        synchronized (mLock) {
-            // ID list creation handles various known failures in device enumeration, so only
-            // exceptions it'll throw are unexpected, and should be propagated upward.
-            return getOrCreateDeviceIdListLocked().toArray(new String[0]);
-        }
+        return CameraManagerGlobal.get().getCameraIdList();
     }
 
     /**
@@ -218,18 +215,10 @@
         CameraCharacteristics characteristics = null;
 
         synchronized (mLock) {
-            if (!getOrCreateDeviceIdListLocked().contains(cameraId)) {
-                throw new IllegalArgumentException(String.format("Camera id %s does not match any" +
-                        " currently connected camera device", cameraId));
-            }
-
-            int id = Integer.parseInt(cameraId);
-
             /*
              * Get the camera characteristics from the camera service directly if it supports it,
              * otherwise get them from the legacy shim instead.
              */
-
             ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
             if (cameraService == null) {
                 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
@@ -239,6 +228,8 @@
                 if (!supportsCamera2ApiLocked(cameraId)) {
                     // Legacy backwards compatibility path; build static info from the camera
                     // parameters
+                    int id = Integer.parseInt(cameraId);
+
                     String parameters = cameraService.getLegacyParameters(id);
 
                     CameraInfo info = cameraService.getCameraInfo(id);
@@ -246,7 +237,7 @@
                     characteristics = LegacyMetadataMapper.createCharacteristics(parameters, info);
                 } else {
                     // Normal path: Get the camera characteristics directly from the camera service
-                    CameraMetadataNative info = cameraService.getCameraCharacteristics(id);
+                    CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId);
 
                     characteristics = new CameraCharacteristics(info);
                 }
@@ -303,14 +294,6 @@
 
             ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
 
-            int id;
-            try {
-                id = Integer.parseInt(cameraId);
-            } catch (NumberFormatException e) {
-                throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: "
-                        + cameraId);
-            }
-
             try {
                 if (supportsCamera2ApiLocked(cameraId)) {
                     // Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
@@ -320,10 +303,18 @@
                             ICameraService.ERROR_DISCONNECTED,
                             "Camera service is currently unavailable");
                     }
-                    cameraUser = cameraService.connectDevice(callbacks, id,
+                    cameraUser = cameraService.connectDevice(callbacks, cameraId,
                             mContext.getOpPackageName(), uid);
                 } else {
                     // Use legacy camera implementation for HAL1 devices
+                    int id;
+                    try {
+                        id = Integer.parseInt(cameraId);
+                    } catch (NumberFormatException e) {
+                        throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: "
+                                + cameraId);
+                    }
+
                     Log.i(TAG, "Using legacy camera HAL.");
                     cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
                 }
@@ -672,69 +663,6 @@
     }
 
     /**
-     * Return or create the list of currently connected camera devices.
-     *
-     * <p>In case of errors connecting to the camera service, will return an empty list.</p>
-     */
-    private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException {
-        if (mDeviceIdList == null) {
-            int numCameras = 0;
-            ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
-            ArrayList<String> deviceIdList = new ArrayList<>();
-
-            // If no camera service, then no devices
-            if (cameraService == null) {
-                return deviceIdList;
-            }
-
-            try {
-                numCameras = cameraService.getNumberOfCameras(CAMERA_TYPE_ALL);
-            } catch(ServiceSpecificException e) {
-                throwAsPublicException(e);
-            } catch (RemoteException e) {
-                // camera service just died - if no camera service, then no devices
-                return deviceIdList;
-            }
-
-            for (int i = 0; i < numCameras; ++i) {
-                // Non-removable cameras use integers starting at 0 for their
-                // identifiers
-                boolean isDeviceSupported = false;
-                try {
-                    CameraMetadataNative info = cameraService.getCameraCharacteristics(i);
-                    if (!info.isEmpty()) {
-                        isDeviceSupported = true;
-                    } else {
-                        throw new AssertionError("Expected to get non-empty characteristics");
-                    }
-                } catch(ServiceSpecificException e) {
-                    // DISCONNECTED means that the HAL reported an low-level error getting the
-                    // device info; ILLEGAL_ARGUMENT means that this devices is not supported.
-                    // Skip listing the device.  Other errors,
-                    // propagate exception onward
-                    if (e.errorCode != ICameraService.ERROR_DISCONNECTED ||
-                            e.errorCode != ICameraService.ERROR_ILLEGAL_ARGUMENT) {
-                        throwAsPublicException(e);
-                    }
-                } catch(RemoteException e) {
-                    // Camera service died - no devices to list
-                    deviceIdList.clear();
-                    return deviceIdList;
-                }
-
-                if (isDeviceSupported) {
-                    deviceIdList.add(String.valueOf(i));
-                } else {
-                    Log.w(TAG, "Error querying camera device " + i + " for listing.");
-                }
-
-            }
-            mDeviceIdList = deviceIdList;
-        }
-        return mDeviceIdList;
-    }
-
-    /**
      * Queries the camera service if it supports the camera2 api directly, or needs a shim.
      *
      * @param cameraId a non-{@code null} camera identifier
@@ -752,8 +680,6 @@
      * @return {@code true} if connecting will work for that device version.
      */
     private boolean supportsCameraApiLocked(String cameraId, int apiVersion) {
-        int id = Integer.parseInt(cameraId);
-
         /*
          * Possible return values:
          * - NO_ERROR => CameraX API is supported
@@ -767,7 +693,7 @@
             // If no camera service, no support
             if (cameraService == null) return false;
 
-            return cameraService.supportsCameraApi(id, apiVersion);
+            return cameraService.supportsCameraApi(cameraId, apiVersion);
         } catch (RemoteException e) {
             // Camera service is now down, no support for any API level
         }
@@ -880,7 +806,10 @@
             }
 
             try {
-                cameraService.addListener(this);
+                CameraStatus[] cameraStatuses = cameraService.addListener(this);
+                for (CameraStatus c : cameraStatuses) {
+                    onStatusChangedLocked(c.status, c.cameraId);
+                }
                 mCameraService = cameraService;
             } catch(ServiceSpecificException e) {
                 // Unexpected failure
@@ -890,6 +819,36 @@
             }
         }
 
+        /**
+         * Get a list of all camera IDs that are at least PRESENT; ignore devices that are
+         * NOT_PRESENT or ENUMERATING, since they cannot be used by anyone.
+         */
+        public String[] getCameraIdList() {
+            String[] cameraIds = null;
+            synchronized(mLock) {
+                // Try to make sure we have an up-to-date list of camera devices.
+                connectCameraServiceLocked();
+
+                int idCount = 0;
+                for (int i = 0; i < mDeviceStatus.size(); i++) {
+                    int status = mDeviceStatus.valueAt(i);
+                    if (status == ICameraServiceListener.STATUS_NOT_PRESENT ||
+                            status == ICameraServiceListener.STATUS_ENUMERATING) continue;
+                    idCount++;
+                }
+                cameraIds = new String[idCount];
+                idCount = 0;
+                for (int i = 0; i < mDeviceStatus.size(); i++) {
+                    int status = mDeviceStatus.valueAt(i);
+                    if (status == ICameraServiceListener.STATUS_NOT_PRESENT ||
+                            status == ICameraServiceListener.STATUS_ENUMERATING) continue;
+                    cameraIds[idCount] = mDeviceStatus.keyAt(i);
+                    idCount++;
+                }
+            }
+            return cameraIds;
+        }
+
         public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException {
             synchronized(mLock) {
 
@@ -1173,9 +1132,9 @@
          * Callback from camera service notifying the process about camera availability changes
          */
         @Override
-        public void onStatusChanged(int status, int cameraId) throws RemoteException {
+        public void onStatusChanged(int status, String cameraId) throws RemoteException {
             synchronized(mLock) {
-                onStatusChangedLocked(status, String.valueOf(cameraId));
+                onStatusChangedLocked(status, cameraId);
             }
         }
 
diff --git a/core/java/android/hardware/location/GeofenceHardware.java b/core/java/android/hardware/location/GeofenceHardware.java
index f537a77..66dd9fc 100644
--- a/core/java/android/hardware/location/GeofenceHardware.java
+++ b/core/java/android/hardware/location/GeofenceHardware.java
@@ -167,6 +167,7 @@
             mMonitorCallbacks = new HashMap<GeofenceHardwareMonitorCallback,
                     GeofenceHardwareMonitorCallbackWrapper>();
 
+    /** @hide */
     public GeofenceHardware(IGeofenceHardware service) {
         mService = service;
     }
diff --git a/core/java/android/net/INetworkRecommendationProvider.aidl b/core/java/android/net/INetworkRecommendationProvider.aidl
index 5e455d3..052c92c 100644
--- a/core/java/android/net/INetworkRecommendationProvider.aidl
+++ b/core/java/android/net/INetworkRecommendationProvider.aidl
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.net.NetworkKey;
 import android.net.RecommendationRequest;
 import android.os.IRemoteCallback;
 
@@ -38,4 +39,15 @@
     void requestRecommendation(in RecommendationRequest request,
                                in IRemoteCallback callback,
                                int sequence);
+
+    /**
+     * Request scoring for networks.
+     *
+     * Implementations should use {@link NetworkScoreManager#updateScores(ScoredNetwork[])} to
+     * respond to score requests.
+     *
+     * @param networks an array of {@link NetworkKey}s to score
+     * @hide
+     */
+    void requestScores(in NetworkKey[] networks);
 }
\ No newline at end of file
diff --git a/core/java/android/net/INetworkScoreService.aidl b/core/java/android/net/INetworkScoreService.aidl
index 24f4504..932f031 100644
--- a/core/java/android/net/INetworkScoreService.aidl
+++ b/core/java/android/net/INetworkScoreService.aidl
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.net.INetworkScoreCache;
+import android.net.NetworkKey;
 import android.net.RecommendationRequest;
 import android.net.RecommendationResult;
 import android.net.ScoredNetwork;
@@ -87,4 +88,16 @@
      */
     RecommendationResult requestRecommendation(in RecommendationRequest request);
 
+    /**
+     * Request scoring for networks.
+     *
+     * Implementations should delegate to the registered network recommendation provider or
+     * fulfill the request locally if possible.
+     *
+     * @param networks an array of {@link NetworkKey}s to score
+     * @return true if the request was delegated or fulfilled locally, false otherwise
+     * @throws SecurityException if the caller is not the system
+     * @hide
+     */
+    boolean requestScores(in NetworkKey[] networks);
 }
diff --git a/core/java/android/net/NetworkRecommendationProvider.java b/core/java/android/net/NetworkRecommendationProvider.java
index fc3213f..af5a052c 100644
--- a/core/java/android/net/NetworkRecommendationProvider.java
+++ b/core/java/android/net/NetworkRecommendationProvider.java
@@ -55,6 +55,15 @@
             ResultCallback callback);
 
     /**
+     * Invoked when network scores have been requested.
+     * <p>
+     * Use {@link NetworkScoreManager#updateScores(ScoredNetwork[])} to respond to score requests.
+     *
+     * @param networks a non-empty array of {@link NetworkKey}s to score.
+     */
+    public abstract void onRequestScores(NetworkKey[] networks);
+
+    /**
      * Services that can handle {@link NetworkScoreManager#ACTION_RECOMMEND_NETWORKS} should
      * return this Binder from their <code>onBind()</code> method.
      */
@@ -118,6 +127,7 @@
 
     private final class ServiceHandler extends Handler {
         static final int MSG_GET_RECOMMENDATION = 1;
+        static final int MSG_REQUEST_SCORES = 2;
 
         ServiceHandler(Looper looper) {
             super(looper, null /*callback*/, true /*async*/);
@@ -136,6 +146,11 @@
                     onRequestRecommendation(request, resultCallback);
                     break;
 
+                case MSG_REQUEST_SCORES:
+                    final NetworkKey[] networks = (NetworkKey[]) msg.obj;
+                    onRequestScores(networks);
+                    break;
+
                 default:
                     throw new IllegalArgumentException("Unknown message: " + what);
             }
@@ -162,5 +177,12 @@
             msg.setData(data);
             msg.sendToTarget();
         }
+
+        @Override
+        public void requestScores(NetworkKey[] networks) throws RemoteException {
+            if (networks != null && networks.length > 0) {
+                mHandler.obtainMessage(ServiceHandler.MSG_REQUEST_SCORES, networks).sendToTarget();
+            }
+        }
     }
 }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 2c0b2a0..0a32f0d 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -253,6 +253,20 @@
     public static final String DISALLOW_REMOVE_USER = "no_remove_user";
 
     /**
+     * Specifies if managed profiles of this user can be removed, other than by its profile owner.
+     * The default value is <code>false</code>.
+     * <p>
+     * This restriction can only be set by device owners.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_REMOVE_MANAGED_PROFILE = "no_remove_managed_profile";
+
+    /**
      * Specifies if a user is disallowed from enabling or
      * accessing debugging features. The default value is <code>false</code>.
      *
@@ -322,8 +336,8 @@
     public static final String DISALLOW_FACTORY_RESET = "no_factory_reset";
 
     /**
-     * Specifies if a user is disallowed from adding new users and
-     * profiles. This can only be set by device owners and profile owners on the primary user.
+     * Specifies if a user is disallowed from adding new users. This can only be set by device
+     * owners and profile owners on the primary user.
      * The default value is <code>false</code>.
      * <p>This restriction has no effect on secondary users and managed profiles since only the
      * primary user can add other users.
@@ -337,6 +351,20 @@
     public static final String DISALLOW_ADD_USER = "no_add_user";
 
     /**
+     * Specifies if a user is disallowed from adding managed profiles.
+     * <p>The default value for an unmanaged user is <code>false</code>.
+     * For users with a device owner set, the default is <code>true</code>
+     * <p>This restriction can only be set by device owners.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
+
+    /**
      * Specifies if a user is disallowed from disabling application
      * verification. The default value is <code>false</code>.
      *
@@ -1406,7 +1434,7 @@
 
     /**
      * Similar to {@link #createProfileForUser(String, int, int, String[])}
-     * except bypassing the checking of {@link UserManager#DISALLOW_ADD_USER}.
+     * except bypassing the checking of {@link UserManager#DISALLOW_ADD_MANAGED_PROFILE}.
      * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      *
      * @see #createProfileForUser(String, int, int, String[])
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
index 1447e7d..466a7e3 100644
--- a/core/java/android/os/UserManagerInternal.java
+++ b/core/java/android/os/UserManagerInternal.java
@@ -120,7 +120,8 @@
     public abstract void onEphemeralUserStop(int userId);
 
     /**
-     * Same as UserManager.createUser(), but bypasses the check for DISALLOW_ADD_USER.
+     * Same as UserManager.createUser(), but bypasses the check for
+     * {@link UserManager#DISALLOW_ADD_USER} and {@link UserManager#DISALLOW_ADD_MANAGED_PROFILE}
      *
      * <p>Called by the {@link com.android.server.devicepolicy.DevicePolicyManagerService} when
      * createAndManageUser is called by the device owner.
@@ -129,7 +130,8 @@
 
     /**
      * Same as {@link UserManager#removeUser(int userHandle)}, but bypasses the check for
-     * {@link UserManager#DISALLOW_REMOVE_USER} and does not require the
+     * {@link UserManager#DISALLOW_REMOVE_USER} and
+     * {@link UserManager#DISALLOW_REMOVE_MANAGED_PROFILE} and does not require the
      * {@link android.Manifest.permission#MANAGE_USERS} permission.
      */
     public abstract boolean removeUserEvenWhenDisallowed(int userId);
diff --git a/core/java/android/text/AndroidCharacter.java b/core/java/android/text/AndroidCharacter.java
index b150b6e..c5f1a01 100644
--- a/core/java/android/text/AndroidCharacter.java
+++ b/core/java/android/text/AndroidCharacter.java
@@ -17,9 +17,11 @@
 package android.text;
 
 /**
- * AndroidCharacter exposes some character properties that are not
- * easily accessed from java.lang.Character.
+ * AndroidCharacter exposes some character properties that used to be not
+ * easily accessed from java.lang.Character, but are now available in ICU.
+ * @deprecated Use various methods from {@link android.icu.lang.UCharacter}, instead.
  */
+@Deprecated
 public class AndroidCharacter
 {
     public static final int EAST_ASIAN_WIDTH_NEUTRAL = 0;
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 6262fc2..58bc9a7 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -22,6 +22,7 @@
 import android.annotation.PluralsRes;
 import android.content.Context;
 import android.content.res.Resources;
+import android.icu.lang.UCharacter;
 import android.icu.util.ULocale;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -545,9 +546,10 @@
         }
 
         public char charAt(int off) {
-            return AndroidCharacter.getMirror(mSource.charAt(mEnd - 1 - off));
+            return (char) UCharacter.getMirror(mSource.charAt(mEnd - 1 - off));
         }
 
+        @SuppressWarnings("deprecation")
         public void getChars(int start, int end, char[] dest, int destoff) {
             TextUtils.getChars(mSource, start + mStart, end + mStart,
                                dest, destoff);
diff --git a/core/java/android/text/style/RasterizerSpan.java b/core/java/android/text/style/RasterizerSpan.java
index cae9640..f0be50a 100644
--- a/core/java/android/text/style/RasterizerSpan.java
+++ b/core/java/android/text/style/RasterizerSpan.java
@@ -19,6 +19,9 @@
 import android.graphics.Rasterizer;
 import android.text.TextPaint;
 
+/**
+ *  @removed Rasterizer is not supported for hw-accerlerated and PDF rendering
+ */
 public class RasterizerSpan extends CharacterStyle implements UpdateAppearance {
 
     private Rasterizer mRasterizer;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3f4c49b..7937ba2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2474,7 +2474,9 @@
      *                     1             PFLAG3_SCROLL_INDICATOR_START
      *                    1              PFLAG3_SCROLL_INDICATOR_END
      *                   1               PFLAG3_ASSIST_BLOCKED
-     *           xxxxxxxx                * NO LONGER NEEDED, SHOULD BE REUSED *
+     *                  1                PFLAG3_CLUSTER
+     *                 1                 PFLAG3_SECTION
+     *           xxxxxx                  * NO LONGER NEEDED, SHOULD BE REUSED *
      *          1                        PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE
      *         1                         PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED
      *        1                          PFLAG3_TEMPORARY_DETACH
@@ -2673,6 +2675,22 @@
     static final int PFLAG3_ASSIST_BLOCKED = 0x4000;
 
     /**
+     * Flag indicating that the view is a root of a keyboard navigation cluster.
+     *
+     * @see #isKeyboardNavigationCluster()
+     * @see #setKeyboardNavigationCluster(boolean)
+     */
+    private static final int PFLAG3_CLUSTER = 0x8000;
+
+    /**
+     * Flag indicating that the view is a root of a keyboard navigation section.
+     *
+     * @see #isKeyboardNavigationSection()
+     * @see #setKeyboardNavigationSection(boolean)
+     */
+    private static final int PFLAG3_SECTION = 0x10000;
+
+    /**
      * Whether this view has rendered elements that overlap (see {@link
      * #hasOverlappingRendering()}, {@link #forceHasOverlappingRendering(boolean)}, and
      * {@link #getHasOverlappingRendering()} ). The value in this bit is only valid when
@@ -3740,6 +3758,16 @@
      */
     int mNextFocusForwardId = View.NO_ID;
 
+    /**
+     * User-specified next keyboard navigation cluster.
+     */
+    int mNextClusterForwardId = View.NO_ID;
+
+    /**
+     * User-specified next keyboard navigation section.
+     */
+    int mNextSectionForwardId = View.NO_ID;
+
     private CheckForLongPress mPendingCheckForLongPress;
     private CheckForTap mPendingCheckForTap = null;
     private PerformClick mPerformClick;
@@ -4531,6 +4559,12 @@
                 case R.styleable.View_nextFocusForward:
                     mNextFocusForwardId = a.getResourceId(attr, View.NO_ID);
                     break;
+                case R.styleable.View_nextClusterForward:
+                    mNextClusterForwardId = a.getResourceId(attr, View.NO_ID);
+                    break;
+                case R.styleable.View_nextSectionForward:
+                    mNextSectionForwardId = a.getResourceId(attr, View.NO_ID);
+                    break;
                 case R.styleable.View_minWidth:
                     mMinWidth = a.getDimensionPixelSize(attr, 0);
                     break;
@@ -4667,10 +4701,19 @@
                         forceHasOverlappingRendering(a.getBoolean(attr, true));
                     }
                     break;
-
                 case R.styleable.View_tooltip:
                     setTooltip(a.getText(attr));
                     break;
+                case R.styleable.View_keyboardNavigationCluster:
+                    if (a.peekValue(attr) != null) {
+                        setKeyboardNavigationCluster(a.getBoolean(attr, true));
+                    }
+                    break;
+                case R.styleable.View_keyboardNavigationSection:
+                    if (a.peekValue(attr) != null) {
+                        setKeyboardNavigationSection(a.getBoolean(attr, true));
+                    }
+                    break;
             }
         }
 
@@ -7852,6 +7895,50 @@
     }
 
     /**
+     * Gets the id of the root of the next keyboard navigation cluster.
+     * @return The next keyboard navigation cluster ID, or {@link #NO_ID} if the framework should
+     * decide automatically.
+     *
+     * @attr ref android.R.styleable#View_nextClusterForward
+     */
+    public int getNextClusterForwardId() {
+        return mNextClusterForwardId;
+    }
+
+    /**
+     * Sets the id of the view to use as the root of the next keyboard navigation cluster.
+     * @param nextClusterForwardId The next cluster ID, or {@link #NO_ID} if the framework should
+     * decide automatically.
+     *
+     * @attr ref android.R.styleable#View_nextClusterForward
+     */
+    public void setNextClusterForwardId(int nextClusterForwardId) {
+        mNextClusterForwardId = nextClusterForwardId;
+    }
+
+    /**
+     * Gets the id of the root of the next keyboard navigation section.
+     * @return The next keyboard navigation section ID, or {@link #NO_ID} if the framework should
+     * decide automatically.
+     *
+     * @attr ref android.R.styleable#View_nextSectionForward
+     */
+    public int getNextSectionForwardId() {
+        return mNextSectionForwardId;
+    }
+
+    /**
+     * Sets the id of the view to use as the root of the next keyboard navigation section.
+     * @param nextSectionForwardId The next section ID, or {@link #NO_ID} if the framework should
+     * decide automatically.
+     *
+     * @attr ref android.R.styleable#View_nextSectionForward
+     */
+    public void setNextSectionForwardId(int nextSectionForwardId) {
+        mNextSectionForwardId = nextSectionForwardId;
+    }
+
+    /**
      * Returns the visibility of this view and all of its ancestors
      *
      * @return True if this view and all of its ancestors are {@link #VISIBLE}
@@ -8947,6 +9034,58 @@
     }
 
     /**
+     * Returns whether this View is a root of a keyboard navigation cluster.
+     *
+     * @return True if this view is a root of a cluster, or false otherwise.
+     * @attr ref android.R.styleable#View_keyboardNavigationCluster
+     */
+    @ViewDebug.ExportedProperty(category = "keyboardNavigationCluster")
+    public final boolean isKeyboardNavigationCluster() {
+        return (mPrivateFlags3 & PFLAG3_CLUSTER) != 0;
+    }
+
+    /**
+     * Set whether this view is a root of a keyboard navigation cluster.
+     *
+     * @param isCluster If true, this view is a root of a cluster.
+     *
+     * @attr ref android.R.styleable#View_keyboardNavigationCluster
+     */
+    public void setKeyboardNavigationCluster(boolean isCluster) {
+        if (isCluster) {
+            mPrivateFlags3 |= PFLAG3_CLUSTER;
+        } else {
+            mPrivateFlags3 &= ~PFLAG3_CLUSTER;
+        }
+    }
+
+    /**
+     * Returns whether this View is a root of a keyboard navigation section.
+     *
+     * @return True if this view is a root of a section, or false otherwise.
+     * @attr ref android.R.styleable#View_keyboardNavigationSection
+     */
+    @ViewDebug.ExportedProperty(category = "keyboardNavigationSection")
+    public final boolean isKeyboardNavigationSection() {
+        return (mPrivateFlags3 & PFLAG3_SECTION) != 0;
+    }
+
+    /**
+     * Set whether this view is a root of a keyboard navigation section.
+     *
+     * @param isSection If true, this view is a root of a section.
+     *
+     * @attr ref android.R.styleable#View_keyboardNavigationSection
+     */
+    public void setKeyboardNavigationSection(boolean isSection) {
+        if (isSection) {
+            mPrivateFlags3 |= PFLAG3_SECTION;
+        } else {
+            mPrivateFlags3 &= ~PFLAG3_SECTION;
+        }
+    }
+
+    /**
      * This method is the last chance for the focused view and its ancestors to
      * respond to an arrow key. This is called when the focused view did not
      * consume the key internally, nor could the view system find a new view in
@@ -13902,11 +14041,6 @@
                     receiver.damageInParent();
                 }
             }
-
-            // Damage the entire IsolatedZVolume receiving this view's shadow.
-            if (isHardwareAccelerated() && getZ() != 0) {
-                damageShadowReceiver();
-            }
         }
     }
 
@@ -13934,23 +14068,6 @@
     }
 
     /**
-     * Damage area of the screen that can be covered by this View's shadow.
-     *
-     * This method will guarantee that any changes to shadows cast by a View
-     * are damaged on the screen for future redraw.
-     */
-    private void damageShadowReceiver() {
-        final AttachInfo ai = mAttachInfo;
-        if (ai != null) {
-            ViewParent p = getParent();
-            if (p != null && p instanceof ViewGroup) {
-                final ViewGroup vg = (ViewGroup) p;
-                vg.damageInParent();
-            }
-        }
-    }
-
-    /**
      * Quick invalidation for View property changes (alpha, translationXY, etc.). We don't want to
      * set any flags or handle all of the cases handled by the default invalidation methods.
      * Instead, we just want to schedule a traversal in ViewRootImpl with the appropriate
@@ -13980,9 +14097,6 @@
         } else {
             damageInParent();
         }
-        if (isHardwareAccelerated() && invalidateParent && getZ() != 0) {
-            damageShadowReceiver();
-        }
     }
 
     /**
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 7dd2fc2..e4d4e7b 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -385,6 +385,14 @@
             }
         }
 
+        if (observer.mOnDrawListeners != null) {
+            if (mOnDrawListeners != null) {
+                mOnDrawListeners.addAll(observer.mOnDrawListeners);
+            } else {
+                mOnDrawListeners = observer.mOnDrawListeners;
+            }
+        }
+
         if (observer.mOnTouchModeChangeListeners != null) {
             if (mOnTouchModeChangeListeners != null) {
                 mOnTouchModeChangeListeners.addAll(observer.mOnTouchModeChangeListeners);
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 8084195..13d5b85 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -350,7 +350,18 @@
                 return;
             }
             if (!mIsEnabled) {
-                throw new IllegalStateException("Accessibility off. Did you forget to check that?");
+                Looper myLooper = Looper.myLooper();
+                if (myLooper == Looper.getMainLooper()) {
+                    throw new IllegalStateException(
+                            "Accessibility off. Did you forget to check that?");
+                } else {
+                    // If we're not running on the thread with the main looper, it's possible for
+                    // the state of accessibility to change between checking isEnabled and
+                    // calling this method. So just log the error rather than throwing the
+                    // exception.
+                    Log.e(LOG_TAG, "Interrupt called with accessibility disabled");
+                    return;
+                }
             }
             userId = mUserId;
         }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0126407..1961bf6 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -681,8 +681,6 @@
     private static final int DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX = 1;
     // Contains the sorted set of desired text sizes in pixels to pick from when auto-sizing text.
     private int[] mAutoSizeTextSizesInPx;
-    // Specifies if the current TextView needs to be auto-sized.
-    private boolean mNeedsTextAutoResize = false;
 
     /**
      * Kick-start the font cache for the zygote process (to pay the cost of
@@ -1562,7 +1560,6 @@
                     }
 
                     Arrays.sort(mAutoSizeTextSizesInPx);
-                    mNeedsTextAutoResize = true;
                     break;
                 default:
                     throw new IllegalArgumentException(
@@ -7525,7 +7522,7 @@
             scrollTo(0, 0);
         }
 
-        if (mNeedsTextAutoResize) {
+        if (isAutoSizeEnabled()) {
             // Call auto-size after the width and height have been calculated.
             autoSizeText();
         }
@@ -7537,20 +7534,21 @@
      * Automatically computes and sets the text size.
      */
     private void autoSizeText() {
+        final int maxWidth = getMeasuredWidth() - getTotalPaddingLeft() - getTotalPaddingRight();
+        final int maxHeight = getMeasuredHeight() - getTotalPaddingBottom() - getTotalPaddingTop();
+
+        if (maxWidth <= 0 || maxHeight <= 0) {
+            return;
+        }
+
         synchronized (TEMP_RECTF) {
             TEMP_RECTF.setEmpty();
-            final int maxWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
-            final int maxHeight = getMeasuredHeight() - getPaddingBottom() - getPaddingTop();
-
-            if (maxWidth <= 0 || maxHeight <= 0) {
-                return;
-            }
-
             TEMP_RECTF.right = maxWidth;
             TEMP_RECTF.bottom = maxHeight;
             final float textSize = findLargestTextSizeWhichFits(TEMP_RECTF);
-            setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-            mNeedsTextAutoResize = false;
+            if (textSize != getTextSize()) {
+                setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+            }
         }
     }
 
@@ -7595,13 +7593,12 @@
 
         if ((mLayout instanceof BoringLayout) && BoringLayout.isBoring(
                 text, mTempTextPaint, getTextDirectionHeuristic(), mBoring) != null) {
-            return mTempTextPaint.getFontSpacing() + getPaddingTop() + getPaddingBottom()
-                    <= availableSpace.bottom
-                    && mTempTextPaint.measureText(text, 0, text.length())
-                    + getPaddingLeft() + getPaddingRight() <= availableSpace.right;
+            return mTempTextPaint.getFontSpacing() <= availableSpace.bottom
+                    && mTempTextPaint.measureText(text, 0, text.length()) <= availableSpace.right;
         } else {
             StaticLayout.Builder layoutBuilder = StaticLayout.Builder.obtain(text, 0, text.length(),
-                    mTempTextPaint, getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
+                    mTempTextPaint,
+                    getMeasuredWidth() - getTotalPaddingLeft() - getTotalPaddingRight());
             layoutBuilder.setAlignment(getLayoutAlignment());
             layoutBuilder.setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier());
             layoutBuilder.setIncludePad(true);
@@ -9271,7 +9268,15 @@
     }
 
     /**
-     * @return {@code true} if this TextView supports autosizing text to fit within its container.
+     * @return {@code true} if this widget supports auto-sizing text and has been configured to
+     * auto-size.
+     */
+    private boolean isAutoSizeEnabled() {
+        return supportsAutoSizeText() && mAutoSizeType != AUTO_SIZE_TYPE_NONE;
+    }
+
+    /**
+     * @return {@code true} if this TextView supports auto-sizing text to fit within its container.
      * @hide
      */
     protected boolean supportsAutoSizeText() {
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 5a134b2..c314cae 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -19,6 +19,7 @@
 import android.animation.ObjectAnimator;
 import android.annotation.NonNull;
 import android.app.Activity;
+import android.app.usage.UsageStatsManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -403,6 +404,7 @@
                 }
             }
         }
+        updateChooserCounts(target, mContentType);
         return super.onTargetSelected(target, alwaysCheck);
     }
 
@@ -547,20 +549,46 @@
         // Do nothing. We'll send the voice stuff ourselves.
     }
 
+    void updateChooserCounts(TargetInfo info, String annotation) {
+        if (info != null) {
+            UsageStatsManager usageStatsManager =
+                    (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
+            if (usageStatsManager == null) {
+                if (DEBUG) {
+                    Log.d(TAG, "Can not start UsageStatsManager");
+                }
+                return;
+            }
+            final ResolveInfo ri = info.getResolveInfo();
+            if (ri != null && ri.activityInfo != null) {
+                usageStatsManager.reportChooserSelection(ri.activityInfo.packageName, getUserId(),
+                        annotation, null, info.getResolvedIntent().getAction());
+                if (DEBUG) {
+                    Log.d(TAG, "ResolveInfo Package is" + ri.activityInfo.packageName);
+                }
+            } else if(DEBUG) {
+                Log.d(TAG, "Can not log Chooser Counts of null ResovleInfo");
+            }
+        }
+    }
+
     void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) {
         if (mRefinementResultReceiver != null) {
             mRefinementResultReceiver.destroy();
             mRefinementResultReceiver = null;
         }
-
         if (selectedTarget == null) {
             Log.e(TAG, "Refinement result intent did not match any known targets; canceling");
         } else if (!checkTargetSourceIntent(selectedTarget, matchingIntent)) {
             Log.e(TAG, "onRefinementResult: Selected target " + selectedTarget
                     + " cannot match refined source intent " + matchingIntent);
-        } else if (super.onTargetSelected(selectedTarget.cloneFilledIn(matchingIntent, 0), false)) {
-            finish();
-            return;
+        } else {
+            TargetInfo clonedTarget = selectedTarget.cloneFilledIn(matchingIntent, 0);
+            if (super.onTargetSelected(clonedTarget, false)) {
+                updateChooserCounts(clonedTarget, mContentType);
+                finish();
+                return;
+            }
         }
         onRefinementCanceled();
     }
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index dd8ef18..c516b5c 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -26,6 +26,7 @@
 import android.app.VoiceInteractor.Prompt;
 import android.content.pm.ComponentInfo;
 import android.os.AsyncTask;
+import android.os.RemoteException;
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -53,7 +54,6 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.PatternMatcher;
-import android.os.RemoteException;
 import android.os.StrictMode;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -114,6 +114,7 @@
     private ComponentName[] mFilteredComponents;
 
     protected ResolverDrawerLayout mResolverDrawerLayout;
+    protected String mContentType;
 
     private boolean mRegistered;
     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@@ -270,6 +271,7 @@
         final String referrerPackage = getReferrerPackageName();
 
         mResolverComparator = new ResolverComparator(this, getTargetIntent(), referrerPackage);
+        mContentType = mResolverComparator.mContentType;
 
         if (configureContentView(mIntents, initialIntents, rList, alwaysUseOption)) {
             return;
diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java
index 4d4c7ce..75be906 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -63,6 +63,8 @@
     private final long mSinceTime;
     private final LinkedHashMap<ComponentName, ScoredTarget> mScoredTargets = new LinkedHashMap<>();
     private final String mReferrerPackage;
+    public String mContentType;
+    private String mAction;
 
     public ResolverComparator(Context context, Intent intent, String referrerPackage) {
         mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
@@ -76,6 +78,8 @@
         mCurrentTime = System.currentTimeMillis();
         mSinceTime = mCurrentTime - USAGE_STATS_PERIOD;
         mStats = mUsm.queryAndAggregateUsageStats(mSinceTime, mCurrentTime);
+        mContentType = intent.getType();
+        mAction = intent.getAction();
     }
 
     public void compute(List<ResolvedComponentInfo> targets) {
@@ -86,6 +90,7 @@
         long mostRecentlyUsedTime = recentSinceTime + 1;
         long mostTimeSpent = 1;
         int mostLaunched = 1;
+        int mostSelected = 1;
 
         for (ResolvedComponentInfo target : targets) {
             final ScoredTarget scoredTarget
@@ -114,6 +119,25 @@
                 if (launched > mostLaunched) {
                     mostLaunched = launched;
                 }
+                // TODO(kanlig): get and combine counts of categories.
+
+                int selected = 0;
+                if (pkStats.mChooserCounts != null && mAction != null
+                        && pkStats.mChooserCounts.get(mAction) != null) {
+                    selected = pkStats.mChooserCounts.get(mAction).getOrDefault(mContentType, 0);
+                }
+                if (DEBUG) {
+                    if (mAction == null) {
+                        Log.d(TAG, "Action type is null");
+                    } else {
+                        Log.d(TAG, "Chooser Count of " + mAction + ":" +
+                                target.name.getPackageName() + " is " + Integer.toString(selected));
+                    }
+                }
+                scoredTarget.chooserCount = selected;
+                if (selected > mostSelected) {
+                    mostSelected = selected;
+                }
             }
         }
 
@@ -190,7 +214,15 @@
                         lhs.activityInfo.packageName, lhs.activityInfo.name));
                 final ScoredTarget rhsTarget = mScoredTargets.get(new ComponentName(
                         rhs.activityInfo.packageName, rhs.activityInfo.name));
-                final float diff = rhsTarget.score - lhsTarget.score;
+
+                final int chooserCountDiff = Long.compare(
+                        rhsTarget.chooserCount, lhsTarget.chooserCount);
+
+                if (chooserCountDiff != 0) {
+                    return chooserCountDiff > 0 ? 1 : -1;
+                }
+
+                final int diff = Float.compare(rhsTarget.score, lhsTarget.score);
 
                 if (diff != 0) {
                     return diff > 0 ? 1 : -1;
@@ -220,6 +252,7 @@
         public long lastTimeUsed;
         public long timeSpent;
         public long launchCount;
+        public long chooserCount;
 
         public ScoredTarget(ComponentInfo ci) {
             componentInfo = ci;
@@ -232,6 +265,7 @@
                     + " lastTimeUsed: " + lastTimeUsed
                     + " timeSpent: " + timeSpent
                     + " launchCount: " + launchCount
+                    + " chooserCount: " + chooserCount
                     + "}";
         }
     }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index e447dd2..44b6e1a 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -136,7 +136,6 @@
     android/graphics/PathEffect.cpp \
     android/graphics/Picture.cpp \
     android/graphics/BitmapRegionDecoder.cpp \
-    android/graphics/Rasterizer.cpp \
     android/graphics/Region.cpp \
     android/graphics/Shader.cpp \
     android/graphics/SurfaceTexture.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 1f810ac..d47c6b7 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -121,13 +121,11 @@
 extern int register_android_graphics_ColorFilter(JNIEnv* env);
 extern int register_android_graphics_DrawFilter(JNIEnv* env);
 extern int register_android_graphics_FontFamily(JNIEnv* env);
-extern int register_android_graphics_LayerRasterizer(JNIEnv*);
 extern int register_android_graphics_Matrix(JNIEnv* env);
 extern int register_android_graphics_Paint(JNIEnv* env);
 extern int register_android_graphics_Path(JNIEnv* env);
 extern int register_android_graphics_PathMeasure(JNIEnv* env);
 extern int register_android_graphics_Picture(JNIEnv*);
-extern int register_android_graphics_Rasterizer(JNIEnv* env);
 extern int register_android_graphics_Region(JNIEnv* env);
 extern int register_android_graphics_SurfaceTexture(JNIEnv* env);
 extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env);
@@ -1332,7 +1330,6 @@
     REG_JNI(register_android_graphics_DrawFilter),
     REG_JNI(register_android_graphics_FontFamily),
     REG_JNI(register_android_graphics_Interpolator),
-    REG_JNI(register_android_graphics_LayerRasterizer),
     REG_JNI(register_android_graphics_MaskFilter),
     REG_JNI(register_android_graphics_Matrix),
     REG_JNI(register_android_graphics_Movie),
@@ -1342,7 +1339,6 @@
     REG_JNI(register_android_graphics_PathMeasure),
     REG_JNI(register_android_graphics_PathEffect),
     REG_JNI(register_android_graphics_Picture),
-    REG_JNI(register_android_graphics_Rasterizer),
     REG_JNI(register_android_graphics_Region),
     REG_JNI(register_android_graphics_Shader),
     REG_JNI(register_android_graphics_SurfaceTexture),
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 178e073..b96e8bd 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -712,6 +712,9 @@
 
 static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) {
     LocalScopedBitmap bitmap(bitmapHandle);
+    if (bitmap->bitmap().isHardware()) {
+        return GraphicsJNI::hardwareLegacyBitmapConfig();
+    }
     return GraphicsJNI::colorTypeToLegacyBitmapConfig(bitmap->info().colorType());
 }
 
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index e86acc4..762a3f3 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -276,6 +276,11 @@
         }
     }
 
+    if (isMutable && isHardware) {
+        doThrowIAE(env, "Bitmaps with Config.HARWARE are always immutable");
+        return nullObjectReturn("Cannot create mutable hardware bitmap");
+    }
+
     // Create the codec.
     NinePatchPeeker peeker;
     std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(streamDeleter.release(),
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index e740428..d8984d3 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -366,6 +366,10 @@
     return c == kHardware_LegacyBitmapConfig;
 }
 
+jint GraphicsJNI::hardwareLegacyBitmapConfig() {
+    return kHardware_LegacyBitmapConfig;
+}
+
 android::Canvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) {
     SkASSERT(env);
     SkASSERT(canvas);
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index ced9939..03dc1fb 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -69,6 +69,7 @@
     static SkColorType getNativeBitmapColorType(JNIEnv*, jobject jconfig);
 
     static bool isHardwareConfig(JNIEnv* env, jobject jconfig);
+    static jint hardwareLegacyBitmapConfig();
 
     static jobject createRegion(JNIEnv* env, SkRegion* region);
 
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 3456839..dfe809d 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -30,7 +30,6 @@
 #include "SkMaskFilter.h"
 #include "SkPath.h"
 #include "SkPathEffect.h"
-#include "SkRasterizer.h"
 #include "SkShader.h"
 #include "SkBlendMode.h"
 #include "unicode/uloc.h"
@@ -848,12 +847,6 @@
         return 0;
     }
 
-    static jlong setRasterizer(jlong objHandle, jlong rasterizerHandle) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        obj->setRasterizer(GraphicsJNI::refNativeRasterizer(rasterizerHandle));
-        return reinterpret_cast<jlong>(obj->getRasterizer());
-    }
-
     static jint getTextAlign(jlong objHandle) {
         Paint* obj = reinterpret_cast<Paint*>(objHandle);
         return static_cast<jint>(obj->getTextAlign());
@@ -1038,7 +1031,6 @@
     {"nSetPathEffect","(JJ)J", (void*) PaintGlue::setPathEffect},
     {"nSetMaskFilter","(JJ)J", (void*) PaintGlue::setMaskFilter},
     {"nSetTypeface","(JJ)J", (void*) PaintGlue::setTypeface},
-    {"nSetRasterizer","(JJ)J", (void*) PaintGlue::setRasterizer},
     {"nGetTextAlign","(J)I", (void*) PaintGlue::getTextAlign},
     {"nSetTextAlign","(JI)V", (void*) PaintGlue::setTextAlign},
     {"nSetTextLocalesByMinikinLangListId","(JI)V",
diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index ab393f2..292454b 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -36,7 +36,18 @@
 class SkPathGlue {
 public:
 
-    static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) {
+    // ---------------- Regular JNI -----------------------------
+
+    static jlong init(JNIEnv* env, jclass clazz) {
+        return reinterpret_cast<jlong>(new SkPath());
+    }
+
+    static jlong init_Path(JNIEnv* env, jclass clazz, jlong valHandle) {
+        SkPath* val = reinterpret_cast<SkPath*>(valHandle);
+        return reinterpret_cast<jlong>(new SkPath(*val));
+    }
+
+    static void finalize(JNIEnv* env, jclass clazz, jlong objHandle) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         // Purge entries from the HWUI path cache if this path's data is unique
         if (obj->unique() && android::uirenderer::Caches::hasInstance()) {
@@ -45,112 +56,68 @@
         delete obj;
     }
 
-    static jlong init1(JNIEnv* env, jobject clazz) {
-        return reinterpret_cast<jlong>(new SkPath());
-    }
-
-    static jlong init2(JNIEnv* env, jobject clazz, jlong valHandle) {
-        SkPath* val = reinterpret_cast<SkPath*>(valHandle);
-        return reinterpret_cast<jlong>(new SkPath(*val));
-    }
-
-    static void reset(JNIEnv* env, jobject clazz, jlong objHandle) {
-        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
-        obj->reset();
-    }
-
-    static void rewind(JNIEnv* env, jobject clazz, jlong objHandle) {
-        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
-        obj->rewind();
-    }
-
-    static void assign(JNIEnv* env, jobject clazz, jlong dstHandle, jlong srcHandle) {
+    static void set(JNIEnv* env, jclass clazz, jlong dstHandle, jlong srcHandle) {
         SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
         const SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
         *dst = *src;
     }
 
-    static jboolean isConvex(JNIEnv* env, jobject clazz, jlong objHandle) {
-        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
-        return obj->isConvex();
-    }
-
-    static jint getFillType(JNIEnv* env, jobject clazz, jlong objHandle) {
-        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
-        return obj->getFillType();
-    }
-
-    static void setFillType(JNIEnv* env, jobject clazz, jlong pathHandle, jint ftHandle) {
-        SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
-        SkPath::FillType ft = static_cast<SkPath::FillType>(ftHandle);
-        path->setFillType(ft);
-    }
-
-    static jboolean isEmpty(JNIEnv* env, jobject clazz, jlong objHandle) {
-        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
-        return obj->isEmpty();
-    }
-
-    static jboolean isRect(JNIEnv* env, jobject clazz, jlong objHandle, jobject jrect) {
-        SkRect rect;
-        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
-        jboolean result = obj->isRect(&rect);
-        GraphicsJNI::rect_to_jrectf(rect, env, jrect);
-        return result;
-    }
-
-    static void computeBounds(JNIEnv* env, jobject clazz, jlong objHandle, jobject jbounds) {
+    static void computeBounds(JNIEnv* env, jclass clazz, jlong objHandle, jobject jbounds) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         const SkRect& bounds = obj->getBounds();
         GraphicsJNI::rect_to_jrectf(bounds, env, jbounds);
     }
 
-    static void incReserve(JNIEnv* env, jobject clazz, jlong objHandle, jint extraPtCount) {
+    static void incReserve(JNIEnv* env, jclass clazz, jlong objHandle, jint extraPtCount) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         obj->incReserve(extraPtCount);
     }
 
-    static void moveTo__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x, jfloat y) {
+    static void moveTo__FF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x, jfloat y) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         obj->moveTo(x, y);
     }
 
-    static void rMoveTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
+    static void rMoveTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx, jfloat dy) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         obj->rMoveTo(dx, dy);
     }
 
-    static void lineTo__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x, jfloat y) {
+    static void lineTo__FF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x, jfloat y) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         obj->lineTo(x, y);
     }
 
-    static void rLineTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
+    static void rLineTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx, jfloat dy) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         obj->rLineTo(dx, dy);
     }
 
-    static void quadTo__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x1, jfloat y1, jfloat x2, jfloat y2) {
+    static void quadTo__FFFF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x1, jfloat y1,
+            jfloat x2, jfloat y2) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         obj->quadTo(x1, y1, x2, y2);
     }
 
-    static void rQuadTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx1, jfloat dy1, jfloat dx2, jfloat dy2) {
+    static void rQuadTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx1, jfloat dy1,
+            jfloat dx2, jfloat dy2) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         obj->rQuadTo(dx1, dy1, dx2, dy2);
     }
 
-    static void cubicTo__FFFFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x1, jfloat y1, jfloat x2, jfloat y2, jfloat x3, jfloat y3) {
+    static void cubicTo__FFFFFF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x1, jfloat y1,
+            jfloat x2, jfloat y2, jfloat x3, jfloat y3) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         obj->cubicTo(x1, y1, x2, y2, x3, y3);
     }
 
-    static void rCubicTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x1, jfloat y1, jfloat x2, jfloat y2, jfloat x3, jfloat y3) {
+    static void rCubicTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x1, jfloat y1,
+            jfloat x2, jfloat y2, jfloat x3, jfloat y3) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         obj->rCubicTo(x1, y1, x2, y2, x3, y3);
     }
 
-    static void arcTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top,
+    static void arcTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat left, jfloat top,
             jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle,
             jboolean forceMoveTo) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
@@ -158,19 +125,19 @@
         obj->arcTo(oval, startAngle, sweepAngle, forceMoveTo);
     }
 
-    static void close(JNIEnv* env, jobject clazz, jlong objHandle) {
+    static void close(JNIEnv* env, jclass clazz, jlong objHandle) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         obj->close();
     }
 
-    static void addRect(JNIEnv* env, jobject clazz, jlong objHandle,
+    static void addRect(JNIEnv* env, jclass clazz, jlong objHandle,
             jfloat left, jfloat top, jfloat right, jfloat bottom, jint dirHandle) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
         obj->addRect(left, top, right, bottom, dir);
     }
 
-    static void addOval(JNIEnv* env, jobject clazz, jlong objHandle,
+    static void addOval(JNIEnv* env, jclass clazz, jlong objHandle,
             jfloat left, jfloat top, jfloat right, jfloat bottom, jint dirHandle) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
@@ -178,20 +145,21 @@
         obj->addOval(oval, dir);
     }
 
-    static void addCircle(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x, jfloat y, jfloat radius, jint dirHandle) {
+    static void addCircle(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x, jfloat y,
+            jfloat radius, jint dirHandle) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
         obj->addCircle(x, y, radius, dir);
     }
 
-    static void addArc(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top,
+    static void addArc(JNIEnv* env, jclass clazz, jlong objHandle, jfloat left, jfloat top,
             jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle) {
         SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         obj->addArc(oval, startAngle, sweepAngle);
     }
 
-    static void addRoundRectXY(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top,
+    static void addRoundRectXY(JNIEnv* env, jclass clazz, jlong objHandle, jfloat left, jfloat top,
             jfloat right, jfloat bottom, jfloat rx, jfloat ry, jint dirHandle) {
         SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
@@ -199,8 +167,8 @@
         obj->addRoundRect(rect, rx, ry, dir);
     }
 
-    static void addRoundRect8(JNIEnv* env, jobject, jlong objHandle, jfloat left, jfloat top,
-            jfloat right, jfloat bottom, jfloatArray array, jint dirHandle) {
+    static void addRoundRect8(JNIEnv* env, jclass clazz, jlong objHandle, jfloat left, jfloat top,
+                jfloat right, jfloat bottom, jfloatArray array, jint dirHandle) {
         SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
@@ -213,49 +181,53 @@
         obj->addRoundRect(rect, src, dir);
     }
 
-    static void addPath__PathFF(JNIEnv* env, jobject clazz, jlong objHandle, jlong srcHandle, jfloat dx, jfloat dy) {
+    static void addPath__PathFF(JNIEnv* env, jclass clazz, jlong objHandle, jlong srcHandle,
+            jfloat dx, jfloat dy) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
         obj->addPath(*src, dx, dy);
     }
 
-    static void addPath__Path(JNIEnv* env, jobject clazz, jlong objHandle, jlong srcHandle) {
+    static void addPath__Path(JNIEnv* env, jclass clazz, jlong objHandle, jlong srcHandle) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
         obj->addPath(*src);
     }
 
-    static void addPath__PathMatrix(JNIEnv* env, jobject clazz, jlong objHandle, jlong srcHandle, jlong matrixHandle) {
+    static void addPath__PathMatrix(JNIEnv* env, jclass clazz, jlong objHandle, jlong srcHandle,
+            jlong matrixHandle) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
         SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
         obj->addPath(*src, *matrix);
     }
 
-    static void offset__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
+    static void offset__FF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx, jfloat dy) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         obj->offset(dx, dy);
     }
 
-    static void setLastPoint(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
+    static void setLastPoint(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx, jfloat dy) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         obj->setLastPt(dx, dy);
     }
 
-    static void transform__MatrixPath(JNIEnv* env, jobject clazz, jlong objHandle, jlong matrixHandle, jlong dstHandle) {
+    static void transform__MatrixPath(JNIEnv* env, jclass clazz, jlong objHandle, jlong matrixHandle,
+            jlong dstHandle) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
         SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
         obj->transform(*matrix, dst);
     }
 
-    static void transform__Matrix(JNIEnv* env, jobject clazz, jlong objHandle, jlong matrixHandle) {
+    static void transform__Matrix(JNIEnv* env, jclass clazz, jlong objHandle, jlong matrixHandle) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
         obj->transform(*matrix);
     }
 
-    static jboolean op(JNIEnv* env, jobject clazz, jlong p1Handle, jlong p2Handle, jint opHandle, jlong rHandle) {
+    static jboolean op(JNIEnv* env, jclass clazz, jlong p1Handle, jlong p2Handle, jint opHandle,
+            jlong rHandle) {
         SkPath* p1  = reinterpret_cast<SkPath*>(p1Handle);
         SkPath* p2  = reinterpret_cast<SkPath*>(p2Handle);
         SkPathOp op = static_cast<SkPathOp>(opHandle);
@@ -416,8 +388,8 @@
     // Note that more than one point may have the same length along the path in
     // the case of a move.
     // NULL can be returned if the Path is empty.
-    static jfloatArray approximate(JNIEnv* env, jclass, jlong pathHandle, float acceptableError)
-    {
+    static jfloatArray approximate(JNIEnv* env, jclass clazz, jlong pathHandle,
+            float acceptableError) {
         SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
         SkASSERT(path);
         SkPath::Iter pathIter(*path, false);
@@ -467,47 +439,94 @@
         delete[] approximation;
         return result;
     }
+
+    // ---------------- @FastNative -----------------------------
+
+    static jboolean isRect(JNIEnv* env, jclass clazz, jlong objHandle, jobject jrect) {
+        SkRect rect;
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        jboolean result = obj->isRect(&rect);
+        GraphicsJNI::rect_to_jrectf(rect, env, jrect);
+        return result;
+    }
+
+    // ---------------- @CriticalNative -------------------------
+
+    static void reset(jlong objHandle) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        obj->reset();
+    }
+
+    static void rewind(jlong objHandle) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        obj->rewind();
+    }
+
+    static jboolean isEmpty(jlong objHandle) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        return obj->isEmpty();
+    }
+
+    static jboolean isConvex(jlong objHandle) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        return obj->isConvex();
+    }
+
+    static jint getFillType(jlong objHandle) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        return obj->getFillType();
+    }
+
+    static void setFillType(jlong pathHandle, jint ftHandle) {;
+        SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+        SkPath::FillType ft = static_cast<SkPath::FillType>(ftHandle);
+        path->setFillType(ft);
+    }
 };
 
 static const JNINativeMethod methods[] = {
-    {"finalizer", "(J)V", (void*) SkPathGlue::finalizer},
-    {"init1","()J", (void*) SkPathGlue::init1},
-    {"init2","(J)J", (void*) SkPathGlue::init2},
-    {"native_reset","(J)V", (void*) SkPathGlue::reset},
-    {"native_rewind","(J)V", (void*) SkPathGlue::rewind},
-    {"native_set","(JJ)V", (void*) SkPathGlue::assign},
-    {"native_isConvex","(J)Z", (void*) SkPathGlue::isConvex},
-    {"native_getFillType","(J)I", (void*) SkPathGlue::getFillType},
-    {"native_setFillType","(JI)V", (void*) SkPathGlue::setFillType},
-    {"native_isEmpty","(J)Z", (void*) SkPathGlue::isEmpty},
-    {"native_isRect","(JLandroid/graphics/RectF;)Z", (void*) SkPathGlue::isRect},
-    {"native_computeBounds","(JLandroid/graphics/RectF;)V", (void*) SkPathGlue::computeBounds},
-    {"native_incReserve","(JI)V", (void*) SkPathGlue::incReserve},
-    {"native_moveTo","(JFF)V", (void*) SkPathGlue::moveTo__FF},
-    {"native_rMoveTo","(JFF)V", (void*) SkPathGlue::rMoveTo},
-    {"native_lineTo","(JFF)V", (void*) SkPathGlue::lineTo__FF},
-    {"native_rLineTo","(JFF)V", (void*) SkPathGlue::rLineTo},
-    {"native_quadTo","(JFFFF)V", (void*) SkPathGlue::quadTo__FFFF},
-    {"native_rQuadTo","(JFFFF)V", (void*) SkPathGlue::rQuadTo},
-    {"native_cubicTo","(JFFFFFF)V", (void*) SkPathGlue::cubicTo__FFFFFF},
-    {"native_rCubicTo","(JFFFFFF)V", (void*) SkPathGlue::rCubicTo},
-    {"native_arcTo","(JFFFFFFZ)V", (void*) SkPathGlue::arcTo},
-    {"native_close","(J)V", (void*) SkPathGlue::close},
-    {"native_addRect","(JFFFFI)V", (void*) SkPathGlue::addRect},
-    {"native_addOval","(JFFFFI)V", (void*) SkPathGlue::addOval},
-    {"native_addCircle","(JFFFI)V", (void*) SkPathGlue::addCircle},
-    {"native_addArc","(JFFFFFF)V", (void*) SkPathGlue::addArc},
-    {"native_addRoundRect","(JFFFFFFI)V", (void*) SkPathGlue::addRoundRectXY},
-    {"native_addRoundRect","(JFFFF[FI)V", (void*) SkPathGlue::addRoundRect8},
-    {"native_addPath","(JJFF)V", (void*) SkPathGlue::addPath__PathFF},
-    {"native_addPath","(JJ)V", (void*) SkPathGlue::addPath__Path},
-    {"native_addPath","(JJJ)V", (void*) SkPathGlue::addPath__PathMatrix},
-    {"native_offset","(JFF)V", (void*) SkPathGlue::offset__FF},
-    {"native_setLastPoint","(JFF)V", (void*) SkPathGlue::setLastPoint},
-    {"native_transform","(JJJ)V", (void*) SkPathGlue::transform__MatrixPath},
-    {"native_transform","(JJ)V", (void*) SkPathGlue::transform__Matrix},
-    {"native_op","(JJIJ)Z", (void*) SkPathGlue::op},
-    {"native_approximate", "(JF)[F", (void*) SkPathGlue::approximate},
+    {"nInit","()J", (void*) SkPathGlue::init},
+    {"nInit","(J)J", (void*) SkPathGlue::init_Path},
+    {"nFinalize", "(J)V", (void*) SkPathGlue::finalize},
+    {"nSet","(JJ)V", (void*) SkPathGlue::set},
+    {"nComputeBounds","(JLandroid/graphics/RectF;)V", (void*) SkPathGlue::computeBounds},
+    {"nIncReserve","(JI)V", (void*) SkPathGlue::incReserve},
+    {"nMoveTo","(JFF)V", (void*) SkPathGlue::moveTo__FF},
+    {"nRMoveTo","(JFF)V", (void*) SkPathGlue::rMoveTo},
+    {"nLineTo","(JFF)V", (void*) SkPathGlue::lineTo__FF},
+    {"nRLineTo","(JFF)V", (void*) SkPathGlue::rLineTo},
+    {"nQuadTo","(JFFFF)V", (void*) SkPathGlue::quadTo__FFFF},
+    {"nRQuadTo","(JFFFF)V", (void*) SkPathGlue::rQuadTo},
+    {"nCubicTo","(JFFFFFF)V", (void*) SkPathGlue::cubicTo__FFFFFF},
+    {"nRCubicTo","(JFFFFFF)V", (void*) SkPathGlue::rCubicTo},
+    {"nArcTo","(JFFFFFFZ)V", (void*) SkPathGlue::arcTo},
+    {"nClose","(J)V", (void*) SkPathGlue::close},
+    {"nAddRect","(JFFFFI)V", (void*) SkPathGlue::addRect},
+    {"nAddOval","(JFFFFI)V", (void*) SkPathGlue::addOval},
+    {"nAddCircle","(JFFFI)V", (void*) SkPathGlue::addCircle},
+    {"nAddArc","(JFFFFFF)V", (void*) SkPathGlue::addArc},
+    {"nAddRoundRect","(JFFFFFFI)V", (void*) SkPathGlue::addRoundRectXY},
+    {"nAddRoundRect","(JFFFF[FI)V", (void*) SkPathGlue::addRoundRect8},
+    {"nAddPath","(JJFF)V", (void*) SkPathGlue::addPath__PathFF},
+    {"nAddPath","(JJ)V", (void*) SkPathGlue::addPath__Path},
+    {"nAddPath","(JJJ)V", (void*) SkPathGlue::addPath__PathMatrix},
+    {"nOffset","(JFF)V", (void*) SkPathGlue::offset__FF},
+    {"nSetLastPoint","(JFF)V", (void*) SkPathGlue::setLastPoint},
+    {"nTransform","(JJJ)V", (void*) SkPathGlue::transform__MatrixPath},
+    {"nTransform","(JJ)V", (void*) SkPathGlue::transform__Matrix},
+    {"nOp","(JJIJ)Z", (void*) SkPathGlue::op},
+    {"nApproximate", "(JF)[F", (void*) SkPathGlue::approximate},
+
+    // ------- @FastNative below here ----------------------
+    {"nIsRect","(JLandroid/graphics/RectF;)Z", (void*) SkPathGlue::isRect},
+
+    // ------- @CriticalNative below here ------------------
+    {"nReset","(J)V", (void*) SkPathGlue::reset},
+    {"nRewind","(J)V", (void*) SkPathGlue::rewind},
+    {"nIsEmpty","(J)Z", (void*) SkPathGlue::isEmpty},
+    {"nIsConvex","(J)Z", (void*) SkPathGlue::isConvex},
+    {"nGetFillType","(J)I", (void*) SkPathGlue::getFillType},
+    {"nSetFillType","(JI)V", (void*) SkPathGlue::setFillType},
 };
 
 int register_android_graphics_Path(JNIEnv* env) {
diff --git a/core/jni/android/graphics/Rasterizer.cpp b/core/jni/android/graphics/Rasterizer.cpp
deleted file mode 100644
index f409498..0000000
--- a/core/jni/android/graphics/Rasterizer.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/* libs/android_runtime/android/graphics/Rasterizer.cpp
-**
-** Copyright 2006, 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.
-*/
-
-// This file was generated from the C++ include file: SkRasterizer.h
-// Any changes made to this file will be discarded by the build.
-// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, 
-// or one of the auxilary file specifications in device/tools/gluemaker.
-
-#include "jni.h"
-#include "GraphicsJNI.h"
-#include "SkLayerRasterizer.h"
-#include "core_jni_helpers.h"
-
-#include <hwui/Paint.h>
-
-// Rasterizer.java holds a pointer (jlong) to this guy
-class NativeRasterizer {
-public:
-    NativeRasterizer() {}
-    virtual ~NativeRasterizer() {}
-
-    // Can return NULL, or a ref to the skia rasterizer.
-    virtual sk_sp<SkRasterizer> refRasterizer() { return NULL; }
-};
-
-class NativeLayerRasterizer : public NativeRasterizer {
-public:
-    SkLayerRasterizer::Builder fBuilder;
-
-    virtual sk_sp<SkRasterizer> refRasterizer() {
-        return fBuilder.snapshot();
-    }
-};
-
-sk_sp<SkRasterizer> GraphicsJNI::refNativeRasterizer(jlong rasterizerHandle) {
-    NativeRasterizer* nr = reinterpret_cast<NativeRasterizer*>(rasterizerHandle);
-    return nr ? nr->refRasterizer() : NULL;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-namespace android {
-
-class SkRasterizerGlue {
-public:
-    static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) {
-        delete reinterpret_cast<NativeRasterizer *>(objHandle);
-    }
-};
-
-static const JNINativeMethod gRasterizerMethods[] = {
-    {"finalizer", "(J)V", (void*) SkRasterizerGlue::finalizer}
-};
-
-int register_android_graphics_Rasterizer(JNIEnv* env) {
-    return RegisterMethodsOrDie(env, "android/graphics/Rasterizer", gRasterizerMethods,
-                                NELEM(gRasterizerMethods));
-}
-
-class SkLayerRasterizerGlue {
-public:
-    static jlong create(JNIEnv* env, jobject) {
-        return reinterpret_cast<jlong>(new NativeLayerRasterizer);
-    }
-
-    static void addLayer(JNIEnv* env, jobject, jlong layerHandle, jlong paintHandle, jfloat dx, jfloat dy) {
-        NativeLayerRasterizer* nr = reinterpret_cast<NativeLayerRasterizer *>(layerHandle);
-        const Paint* paint = reinterpret_cast<Paint *>(paintHandle);
-        SkASSERT(nr);
-        SkASSERT(paint);
-        nr->fBuilder.addLayer(*paint, dx, dy);
-    }
-};
-
-static const JNINativeMethod gLayerRasterizerMethods[] = {
-    { "nativeConstructor",  "()J",      (void*)SkLayerRasterizerGlue::create    },
-    { "nativeAddLayer",     "(JJFF)V",  (void*)SkLayerRasterizerGlue::addLayer  }
-};
-
-int register_android_graphics_LayerRasterizer(JNIEnv* env)
-{
-    return RegisterMethodsOrDie(env, "android/graphics/LayerRasterizer",
-                                gLayerRasterizerMethods, NELEM(gLayerRasterizerMethods));
-}
-
-}
-
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index a31bd80..be9449b 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -185,12 +185,12 @@
 
 // SkRegion::Op and SkClipOp are numerically identical, so we can freely cast
 // from one to the other (though SkClipOp is destined to become a strict subset)
-static_assert(SkRegion::kDifference_Op == static_cast<SkRegion::Op>(kDifference_SkClipOp), "");
-static_assert(SkRegion::kIntersect_Op == static_cast<SkRegion::Op>(kIntersect_SkClipOp), "");
-static_assert(SkRegion::kUnion_Op == static_cast<SkRegion::Op>(kUnion_SkClipOp), "");
-static_assert(SkRegion::kXOR_Op == static_cast<SkRegion::Op>(kXOR_SkClipOp), "");
-static_assert(SkRegion::kReverseDifference_Op == static_cast<SkRegion::Op>(kReverseDifference_SkClipOp), "");
-static_assert(SkRegion::kReplace_Op == static_cast<SkRegion::Op>(kReplace_SkClipOp), "");
+static_assert(SkRegion::kDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kDifference), "");
+static_assert(SkRegion::kIntersect_Op == static_cast<SkRegion::Op>(SkClipOp::kIntersect), "");
+static_assert(SkRegion::kUnion_Op == static_cast<SkRegion::Op>(SkClipOp::kUnion), "");
+static_assert(SkRegion::kXOR_Op == static_cast<SkRegion::Op>(SkClipOp::kXOR), "");
+static_assert(SkRegion::kReverseDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kReverseDifference), "");
+static_assert(SkRegion::kReplace_Op == static_cast<SkRegion::Op>(SkClipOp::kReplace), "");
 
 static SkClipOp opHandleToClipOp(jint opHandle) {
     // The opHandle is defined in Canvas.java to be Region::Op
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index 53c9ff0..b6b5245 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -197,7 +197,7 @@
     Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
     nativeCanvas->setBitmap(bitmap);
     nativeCanvas->clipRect(rect.left, rect.top, rect.right, rect.bottom,
-            kIntersect_SkClipOp);
+            SkClipOp::kIntersect);
 
     if (dirtyRect) {
         INVOKEV(dirtyRect, gRectClassInfo.set,
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 63997e5..92693b7 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -339,7 +339,7 @@
 
     if (dirtyRectPtr) {
         nativeCanvas->clipRect(dirtyRect.left, dirtyRect.top,
-                dirtyRect.right, dirtyRect.bottom, kIntersect_SkClipOp);
+                dirtyRect.right, dirtyRect.bottom, SkClipOp::kIntersect);
     }
 
     if (dirtyRectObj) {
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index 351dce9..3c8db7f 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -167,7 +167,7 @@
     Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
     nativeCanvas->setBitmap(bitmap);
     nativeCanvas->clipRect(rect.left, rect.top, rect.right, rect.bottom,
-            kIntersect_SkClipOp);
+            SkClipOp::kIntersect);
 
     if (dirtyRect) {
         INVOKEV(dirtyRect, gRectClassInfo.set,
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index d13d154..ac7b236 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -65,4 +65,5 @@
 
     <!-- The small screens of watch devices makes multi-window support undesireable. -->
     <bool name="config_supportsMultiWindow">false</bool>
+    <bool name="config_supportsSplitScreenMultiWindow">false</bool>
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8c64484..aa050de 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2887,6 +2887,28 @@
         <!-- Defines text displayed in a small popup window on hover or long press. -->
         <attr name="tooltip" format="string" localization="suggested" />
 
+        <!-- Whether this view is a root of a keyboard navigation cluster.
+             See {@link android.view.View#setKeyboardNavigationCluster(boolean)}. -->
+        <attr name="keyboardNavigationCluster" format="boolean" />
+
+        <!-- Whether this view is a root of a keyboard navigation section.
+             See {@link android.view.View#setKeyboardNavigationSection(boolean)}. -->
+        <attr name="keyboardNavigationSection" format="boolean" />
+
+        <!-- Defines the next keyboard navigation cluster.
+
+             If the reference refers to a view that does not exist or is part
+             of a hierarchy that is invisible, a {@link java.lang.RuntimeException}
+             will result when the reference is accessed.-->
+        <attr name="nextClusterForward" format="reference"/>
+
+        <!-- Defines the next keyboard navigation section.
+
+             If the reference refers to a view that does not exist or is part
+             of a hierarchy that is invisible, a {@link java.lang.RuntimeException}
+             will result when the reference is accessed.-->
+        <attr name="nextSectionForward" format="reference"/>
+
     </declare-styleable>
 
     <!-- Attributes that can be assigned to a tag for a particular View. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9e75ff9..69c7b60 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2568,6 +2568,9 @@
          E.g. freeform, split-screen, picture-in-picture. -->
     <bool name="config_supportsMultiWindow">true</bool>
 
+    <!-- True if the device supports split screen as a form of multi-window. -->
+    <bool name="config_supportsSplitScreenMultiWindow">true</bool>
+
     <!-- True if the device requires AppWidgetService even if it does not have
          the PackageManager.FEATURE_APP_WIDGETS feature -->
     <bool name="config_enableAppWidgetService">false</bool>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 37c4fd1..4604f3f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2776,6 +2776,10 @@
         <public name="paddingHorizontal" />
         <public name="paddingVertical" />
         <public name="visibleToEphemeral" />
+        <public name="keyboardNavigationCluster" />
+        <public name="keyboardNavigationSection" />
+        <public name="nextClusterForward" />
+        <public name="nextSectionForward" />
     </public-group>
 
     <public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 87f38c1..10badab 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -741,17 +741,11 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readSms">read your text messages (SMS or MMS)</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_readSms" product="tablet">Allows the app to read SMS
-      messages stored on your tablet or SIM card. This allows the app to read all
-      SMS messages, regardless of content or confidentiality.</string>
+    <string name="permdesc_readSms" product="tablet">This app can read all SMS (text) messages stored on your tablet.</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_readSms" product="tv">Allows the app to read SMS
-      messages stored on your TV or SIM card. This allows the app to read all
-      SMS messages, regardless of content or confidentiality.</string>
+    <string name="permdesc_readSms" product="tv">This app can read all SMS (text) messages stored on your TV.</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_readSms" product="default">Allows the app to read SMS
-      messages stored on your phone or SIM card. This allows the app to read all
-      SMS messages, regardless of content or confidentiality.</string>
+    <string name="permdesc_readSms" product="default">This app can read all SMS (text) messages stored on your phone.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_receiveWapPush">receive text messages (WAP)</string>
@@ -793,12 +787,9 @@
       running.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_systemAlertWindow">draw over other apps</string>
+    <string name="permlab_systemAlertWindow">This app can appear on top of other apps</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_systemAlertWindow">Allows the app to draw on top of other
-        applications or parts of the user interface.  They may interfere with your
-        use of the interface in any application, or change what you think you are
-        seeing in other applications.</string>
+    <string name="permdesc_systemAlertWindow">This app can appear on top of other apps or other parts of the screen. This may interfere with normal app usage and change the way that other apps appear.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_persistentActivity">make app always run</string>
@@ -904,20 +895,7 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readCallLog">read call log</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_readCallLog" product="tablet">Allows the app to read
-     your tablet\'s call log, including data about incoming and outgoing calls.
-     This permission allows apps to save your call log data, and malicious apps
-     may share call log data without your knowledge.</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_readCallLog" product="tv">Allows the app to read
-     your TV\'s call log, including data about incoming and outgoing calls.
-     This permission allows apps to save your call log data, and malicious apps
-     may share call log data without your knowledge.</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_readCallLog" product="default">Allows the app to read
-      your phone\'s call log, including data about incoming and outgoing calls.
-      This permission allows apps to save your call log data, and malicious apps
-      may share call log data without your knowledge.</string>
+    <string name="permdesc_readCallLog">This app can read your call history.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_writeCallLog">write call log</string>
@@ -939,46 +917,24 @@
     that monitor your physical condition, such as your heart rate.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_readCalendar">read calendar events plus confidential information</string>
+    <string name="permlab_readCalendar">Read calendar events and details</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readCalendar" product="tablet">Allows the app to read
-       all calendar events stored on your tablet, including those of friends or
-       co-workers. This may allow the app to share or save your calendar data,
-       regardless of confidentiality or sensitivity.</string>
+    <string name="permdesc_readCalendar" product="tablet">This app can read all calendar events stored on your tablet and share or save your calendar data.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readCalendar" product="tv">Allows the app to read
-       all calendar events stored on your TV, including those of friends or
-       co-workers. This may allow the app to share or save your calendar data,
-       regardless of confidentiality or sensitivity.</string>
+    <string name="permdesc_readCalendar" product="tv">This app can read all calendar events stored on your TV and share or save your calendar data.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readCalendar" product="default">Allows the app to
-       read all calendar events stored on your phone, including those of friends
-       or co-workers. This may allow the app to share or save your calendar data,
-       regardless of confidentiality or sensitivity.</string>
+    <string name="permdesc_readCalendar" product="default">This app can read all calendar events stored on your phone and share or save your calendar data.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_writeCalendar">add or modify calendar events and send email to guests without owners\' knowledge</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_writeCalendar" product="tablet">Allows the app to
-        add, remove, change events that you can modify on your tablet, including
-        those of friends or co-workers. This may allow the app to send messages
-        that appear to come from calendar owners, or modify events without the
-        owners\' knowledge.</string>
+    <string name="permdesc_writeCalendar" product="tablet">This app can add, remove, or change calendar events on your tablet. This app can send messages that may appear to come from calendar owners, or change events without notifying their owners.</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_writeCalendar" product="tv">Allows the app to
-        add, remove, change events that you can modify on your TV, including
-        those of friends or co-workers. This may allow the app to send messages
-        that appear to come from calendar owners, or modify events without the
-        owners\' knowledge.</string>
+    <string name="permdesc_writeCalendar" product="tv">This app can add, remove, or change calendar events on your TV. This app can send messages that may appear to come from calendar owners, or change events without notifying their owners.</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_writeCalendar" product="default">Allows the app to
-        add, remove, change events that you can modify on your phone, including
-        those of friends or co-workers. This may allow the app to send messages
-        that appear to come from calendar owners, or modify events without the
-        owners\' knowledge.</string>
+    <string name="permdesc_writeCalendar" product="default">This app can add, remove, or change calendar events on your phone. This app can send messages that may appear to come from calendar owners, or change events without notifying their owners.</string>
 
-
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the applicatfion to do this. -->
     <string name="permlab_accessLocationExtraCommands">access extra location provider commands</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_accessLocationExtraCommands">Allows the app to access
@@ -989,23 +945,17 @@
     <string name="permlab_accessFineLocation">access precise location (GPS and
       network-based)</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_accessFineLocation">Allows the app to get your
-      precise location using the Global Positioning System (GPS) or network
-      location sources such as cell towers and Wi-Fi. These location services
-      must be turned on and available to your device for the app to use them.
-      Apps may use this to determine where you are, and may consume additional
-      battery power.</string>
+    <string name="permdesc_accessFineLocation">This app can get your location based on GPS or network location sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_accessCoarseLocation">access approximate location
       (network-based)</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_accessCoarseLocation">Allows the app to get your
-      approximate location. This location is derived by location services using
-      network location sources such as cell towers and Wi-Fi. These location
-      services must be turned on and available to your device for the app to
-      use them. Apps may use this to determine approximately where you
-      are.</string>
+    <string name="permdesc_accessCoarseLocation" product="tablet">This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them.</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_accessCoarseLocation" product="tv">This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your TV for the app to be able to use them.</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_accessCoarseLocation" product="default">This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them.</string>
 
  <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_modifyAudioSettings">change your audio settings</string>
@@ -1015,9 +965,7 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_recordAudio">record audio</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_recordAudio">Allows the app to record audio with the
-      microphone.  This permission allows the app to record audio at any time
-      without your confirmation.</string>
+    <string name="permdesc_recordAudio">This app can record audio using the microphone at any time.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_sim_communication">send commands to the SIM</string>
@@ -1027,9 +975,7 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_camera">take pictures and videos</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_camera">Allows the app to take pictures and videos
-      with the camera.  This permission allows the app to use the camera at any
-      time without your confirmation.</string>
+    <string name="permdesc_camera">This app can take pictures and record videos using the camera at any time.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_vibrate">control vibration</string>
@@ -4277,7 +4223,7 @@
     <string name="zen_mode_alarm">Until <xliff:g id="formattedTime" example="10:00 PM">%1$s</xliff:g> (next alarm)</string>
 
     <!-- Zen mode condition: no exit criteria. [CHAR LIMIT=NONE] -->
-    <string name="zen_mode_forever">Until you turn this off</string>
+    <string name="zen_mode_forever">Until you turn off Do Not Disturb</string>
 
     <!-- Zen mode condition: no exit criteria, includes the name of the feature for emphasis. [CHAR LIMIT=NONE] -->
     <string name="zen_mode_forever_dnd">Until you turn off Do Not Disturb</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a85ddf1..dcb44d3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -307,6 +307,7 @@
   <java-symbol type="bool" name="config_supportAudioSourceUnprocessed" />
   <java-symbol type="bool" name="config_freeformWindowManagement" />
   <java-symbol type="bool" name="config_supportsMultiWindow" />
+  <java-symbol type="bool" name="config_supportsSplitScreenMultiWindow" />
   <java-symbol type="bool" name="config_guestUserEphemeral" />
   <java-symbol type="bool" name="config_localDisplaysMirrorContent" />
   <java-symbol type="integer" name="config_defaultDisplayDefaultColorMode" />
diff --git a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
index 5ac8f56..9a81401 100644
--- a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
+++ b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
@@ -28,7 +28,9 @@
     private NetworkRecProvider mRecProvider;
     private Handler mHandler;
     private INetworkRecommendationProvider mStub;
-    private CountDownLatch mCountDownLatch;
+    private CountDownLatch mRecRequestLatch;
+    private CountDownLatch mScoreRequestLatch;
+    private NetworkKey[] mTestNetworkKeys;
 
     @Override
     public void setUp() throws Exception {
@@ -45,20 +47,24 @@
 
         HandlerThread thread = new HandlerThread("NetworkRecommendationProviderTest");
         thread.start();
-        mCountDownLatch = new CountDownLatch(1);
+        mRecRequestLatch = new CountDownLatch(1);
+        mScoreRequestLatch = new CountDownLatch(1);
         mHandler = new Handler(thread.getLooper());
-        mRecProvider = new NetworkRecProvider(mHandler, mCountDownLatch);
+        mRecProvider = new NetworkRecProvider(mHandler, mRecRequestLatch, mScoreRequestLatch);
         mStub = INetworkRecommendationProvider.Stub.asInterface(mRecProvider.getBinder());
+        mTestNetworkKeys = new NetworkKey[2];
+        mTestNetworkKeys[0] = new NetworkKey(new WifiKey("\"ssid_01\"", "00:00:00:00:00:11"));
+        mTestNetworkKeys[1] = new NetworkKey(new WifiKey("\"ssid_02\"", "00:00:00:00:00:22"));
     }
 
     @MediumTest
-    public void testRequestReceived() throws Exception {
+    public void testRecommendationRequestReceived() throws Exception {
         final RecommendationRequest request = new RecommendationRequest.Builder().build();
         final int sequence = 100;
         mStub.requestRecommendation(request, mMockRemoteCallback, sequence);
 
         // wait for onRequestRecommendation() to be called in our impl below.
-        mCountDownLatch.await(200, TimeUnit.MILLISECONDS);
+        mRecRequestLatch.await(200, TimeUnit.MILLISECONDS);
         NetworkRecommendationProvider.ResultCallback expectedResultCallback =
                 new NetworkRecommendationProvider.ResultCallback(mMockRemoteCallback, sequence);
         assertEquals(request, mRecProvider.mCapturedRequest);
@@ -98,14 +104,44 @@
         }
     }
 
+    @MediumTest
+    public void testScoreRequestReceived() throws Exception {
+        mStub.requestScores(mTestNetworkKeys);
+
+        // wait for onRequestScores() to be called in our impl below.
+        mScoreRequestLatch.await(200, TimeUnit.MILLISECONDS);
+
+        assertSame(mTestNetworkKeys, mRecProvider.mCapturedNetworks);
+    }
+
+    @MediumTest
+    public void testScoreRequest_nullInput() throws Exception {
+        mStub.requestScores(null);
+
+        // onRequestScores() should never be called
+        assertFalse(mScoreRequestLatch.await(200, TimeUnit.MILLISECONDS));
+    }
+
+    @MediumTest
+    public void testScoreRequest_emptyInput() throws Exception {
+        mStub.requestScores(new NetworkKey[0]);
+
+        // onRequestScores() should never be called
+        assertFalse(mScoreRequestLatch.await(200, TimeUnit.MILLISECONDS));
+    }
+
     private static class NetworkRecProvider extends NetworkRecommendationProvider {
-        private final CountDownLatch mCountDownLatch;
+        private final CountDownLatch mRecRequestLatch;
+        private final CountDownLatch mScoreRequestLatch;
         RecommendationRequest mCapturedRequest;
         ResultCallback mCapturedCallback;
+        NetworkKey[] mCapturedNetworks;
 
-        NetworkRecProvider(Handler handler, CountDownLatch countDownLatch) {
+        NetworkRecProvider(Handler handler, CountDownLatch recRequestLatch,
+            CountDownLatch networkRequestLatch) {
             super(handler);
-            mCountDownLatch = countDownLatch;
+            mRecRequestLatch = recRequestLatch;
+            mScoreRequestLatch = networkRequestLatch;
         }
 
         @Override
@@ -113,7 +149,13 @@
                 ResultCallback callback) {
             mCapturedRequest = request;
             mCapturedCallback = callback;
-            mCountDownLatch.countDown();
+            mRecRequestLatch.countDown();
+        }
+
+        @Override
+        public void onRequestScores(NetworkKey[] networks) {
+            mCapturedNetworks = networks;
+            mScoreRequestLatch.countDown();
         }
     }
 }
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 756087c..cd75fe9 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -467,7 +467,11 @@
 
 
         /**
-          * @hide
+         * Special configuration, when bitmap is stored only in graphic memory.
+         * Bitmaps in this configuration are always immutable.
+         *
+         * It is optimal for cases, when the only operation with the bitmap is to draw it on a
+         * screen.
          */
         HARDWARE    (6);
 
@@ -810,7 +814,8 @@
      * @param width    The width of the bitmap
      * @param height   The height of the bitmap
      * @param config   The bitmap config to create.
-     * @throws IllegalArgumentException if the width or height are <= 0
+     * @throws IllegalArgumentException if the width or height are <= 0, or if
+     *         Config is Config.HARDWARE, because hardware bitmaps are always immutable
      */
     public static Bitmap createBitmap(int width, int height, Config config) {
         return createBitmap(width, height, config, true);
@@ -825,7 +830,8 @@
      * @param width    The width of the bitmap
      * @param height   The height of the bitmap
      * @param config   The bitmap config to create.
-     * @throws IllegalArgumentException if the width or height are <= 0
+     * @throws IllegalArgumentException if the width or height are <= 0, or if
+     *         Config is Config.HARDWARE, because hardware bitmaps are always immutable
      */
     public static Bitmap createBitmap(DisplayMetrics display, int width,
             int height, Config config) {
@@ -843,7 +849,8 @@
      *                 bitmap as opaque. Doing so will clear the bitmap in black
      *                 instead of transparent.
      *
-     * @throws IllegalArgumentException if the width or height are <= 0
+     * @throws IllegalArgumentException if the width or height are <= 0, or if
+     *         Config is Config.HARDWARE, because hardware bitmaps are always immutable
      */
     private static Bitmap createBitmap(int width, int height, Config config, boolean hasAlpha) {
         return createBitmap(null, width, height, config, hasAlpha);
@@ -862,13 +869,17 @@
      *                 bitmap as opaque. Doing so will clear the bitmap in black
      *                 instead of transparent.
      *
-     * @throws IllegalArgumentException if the width or height are <= 0
+     * @throws IllegalArgumentException if the width or height are <= 0, or if
+     *         Config is Config.HARDWARE, because hardware bitmaps are always immutable
      */
     private static Bitmap createBitmap(DisplayMetrics display, int width, int height,
             Config config, boolean hasAlpha) {
         if (width <= 0 || height <= 0) {
             throw new IllegalArgumentException("width and height must be > 0");
         }
+        if (config == Config.HARDWARE) {
+            throw new IllegalArgumentException("can't create mutable bitmap with Config.HARDWARE");
+        }
         Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true);
         if (display != null) {
             bm.mDensity = display.densityDpi;
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 447a4c4..a5517f0 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import static android.graphics.BitmapFactory.Options.validate;
+
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.os.Trace;
@@ -103,6 +105,9 @@
          * If set, decode methods will always return a mutable Bitmap instead of
          * an immutable one. This can be used for instance to programmatically apply
          * effects to a Bitmap loaded through BitmapFactory.
+         * <p>Can not be set simultaneously with inPreferredConfig =
+         * {@link android.graphics.Bitmap.Config#HARDWARE},
+         * because hardware bitmaps are always immutable.
          */
         @SuppressWarnings({"UnusedDeclaration"}) // used in native code
         public boolean inMutable;
@@ -381,6 +386,12 @@
         public void requestCancelDecode() {
             mCancel = true;
         }
+
+        static void validate(Options opts) {
+            if (opts != null && opts.inMutable && opts.inPreferredConfig == Bitmap.Config.HARDWARE) {
+                throw new IllegalArgumentException("Bitmaps with Config.HARWARE are always immutable");
+            }
+        }
     }
 
     /**
@@ -393,8 +404,12 @@
      * @return The decoded bitmap, or null if the image data could not be
      *         decoded, or, if opts is non-null, if opts requested only the
      *         size be returned (in opts.outWidth and opts.outHeight)
+     * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+     *         is {@link android.graphics.Bitmap.Config#HARDWARE}
+     *         and {@link BitmapFactory.Options#inMutable} is set.
      */
     public static Bitmap decodeFile(String pathName, Options opts) {
+        validate(opts);
         Bitmap bm = null;
         InputStream stream = null;
         try {
@@ -431,10 +446,13 @@
     /**
      * Decode a new Bitmap from an InputStream. This InputStream was obtained from
      * resources, which we pass to be able to scale the bitmap accordingly.
+     * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+     *         is {@link android.graphics.Bitmap.Config#HARDWARE}
+     *         and {@link BitmapFactory.Options#inMutable} is set.
      */
     public static Bitmap decodeResourceStream(Resources res, TypedValue value,
             InputStream is, Rect pad, Options opts) {
-
+        validate(opts);
         if (opts == null) {
             opts = new Options();
         }
@@ -466,8 +484,12 @@
      * @return The decoded bitmap, or null if the image data could not be
      *         decoded, or, if opts is non-null, if opts requested only the
      *         size be returned (in opts.outWidth and opts.outHeight)
+     * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+     *         is {@link android.graphics.Bitmap.Config#HARDWARE}
+     *         and {@link BitmapFactory.Options#inMutable} is set.
      */
     public static Bitmap decodeResource(Resources res, int id, Options opts) {
+        validate(opts);
         Bitmap bm = null;
         InputStream is = null; 
         
@@ -520,11 +542,15 @@
      * @return The decoded bitmap, or null if the image data could not be
      *         decoded, or, if opts is non-null, if opts requested only the
      *         size be returned (in opts.outWidth and opts.outHeight)
+     * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+     *         is {@link android.graphics.Bitmap.Config#HARDWARE}
+     *         and {@link BitmapFactory.Options#inMutable} is set.
      */
     public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) {
         if ((offset | length) < 0 || data.length < offset + length) {
             throw new ArrayIndexOutOfBoundsException();
         }
+        validate(opts);
 
         Bitmap bm;
 
@@ -598,6 +624,9 @@
      * @return The decoded bitmap, or null if the image data could not be
      *         decoded, or, if opts is non-null, if opts requested only the
      *         size be returned (in opts.outWidth and opts.outHeight)
+     * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+     *         is {@link android.graphics.Bitmap.Config#HARDWARE}
+     *         and {@link BitmapFactory.Options#inMutable} is set.
      *
      * <p class="note">Prior to {@link android.os.Build.VERSION_CODES#KITKAT},
      * if {@link InputStream#markSupported is.markSupported()} returns true,
@@ -610,6 +639,7 @@
         if (is == null) {
             return null;
         }
+        validate(opts);
 
         Bitmap bm = null;
 
@@ -673,8 +703,12 @@
      * @param opts null-ok; Options that control downsampling and whether the
      *             image should be completely decoded, or just its size returned.
      * @return the decoded bitmap, or null
+     * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+     *         is {@link android.graphics.Bitmap.Config#HARDWARE}
+     *         and {@link BitmapFactory.Options#inMutable} is set.
      */
     public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
+        validate(opts);
         Bitmap bm;
 
         Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeFileDescriptor");
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
index e689b08..04abca1 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -178,8 +178,12 @@
      *             inPurgeable is not supported.
      * @return The decoded bitmap, or null if the image data could not be
      *         decoded.
+     * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+     *         is {@link android.graphics.Bitmap.Config#HARDWARE}
+     *         and {@link BitmapFactory.Options#inMutable} is set.
      */
     public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
+        BitmapFactory.Options.validate(options);
         synchronized (mNativeLock) {
             checkRecycled("decodeRegion called on recycled region decoder");
             if (rect.right <= 0 || rect.bottom <= 0 || rect.left >= getWidth()
diff --git a/graphics/java/android/graphics/LayerRasterizer.java b/graphics/java/android/graphics/LayerRasterizer.java
index b692ecf..25155ab 100644
--- a/graphics/java/android/graphics/LayerRasterizer.java
+++ b/graphics/java/android/graphics/LayerRasterizer.java
@@ -16,26 +16,20 @@
 
 package android.graphics;
 
+/**
+ * @removed feature is not supported by hw-accerlerated or PDF backends
+ */
 @Deprecated
 public class LayerRasterizer extends Rasterizer {
-    public LayerRasterizer() {
-        native_instance = nativeConstructor();
-    }
+    public LayerRasterizer() { }
 
     /** Add a new layer (above any previous layers) to the rasterizer.
         The layer will extract those fields that affect the mask from
         the specified paint, but will not retain a reference to the paint
         object itself, so it may be reused without danger of side-effects.
     */
-    public void addLayer(Paint paint, float dx, float dy) {
-        nativeAddLayer(native_instance, paint.getNativeInstance(), dx, dy);
-    }
+    public void addLayer(Paint paint, float dx, float dy) { }
 
-    public void addLayer(Paint paint) {
-        nativeAddLayer(native_instance, paint.getNativeInstance(), 0, 0);
-    }
-
-    private static native long nativeConstructor();
-    private static native void nativeAddLayer(long native_layer, long native_paint, float dx, float dy);
+    public void addLayer(Paint paint) { }
 }
 
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index aa81b91..3e59f34 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -58,8 +58,12 @@
     @Mode
     public int mMode = MODE_EMPTY;
 
-    /** @hide */
-    public final Path mPath = new Path();
+    /**
+     * Only guaranteed to be non-null when mode == MODE_CONVEX_PATH
+     *
+     * @hide
+     */
+    public Path mPath;
 
     /** @hide */
     public final Rect mRect = new Rect();
@@ -87,8 +91,11 @@
      * @see #isEmpty()
      */
     public void setEmpty() {
+        if (mPath != null) {
+            // rewind here to avoid thrashing the allocations, but could alternately clear ref
+            mPath.rewind();
+        }
         mMode = MODE_EMPTY;
-        mPath.rewind();
         mRect.setEmpty();
         mRadius = RADIUS_UNDEFINED;
     }
@@ -148,7 +155,12 @@
      */
     public void set(@NonNull Outline src) {
         mMode = src.mMode;
-        mPath.set(src.mPath);
+        if (src.mMode == MODE_CONVEX_PATH) {
+            if (mPath == null) {
+                mPath = new Path();
+            }
+            mPath.set(src.mPath);
+        }
         mRect.set(src.mRect);
         mRadius = src.mRadius;
         mAlpha = src.mAlpha;
@@ -180,10 +192,13 @@
             return;
         }
 
+        if (mMode == MODE_CONVEX_PATH) {
+            // rewind here to avoid thrashing the allocations, but could alternately clear ref
+            mPath.rewind();
+        }
         mMode = MODE_ROUND_RECT;
         mRect.set(left, top, right, bottom);
         mRadius = radius;
-        mPath.rewind();
     }
 
     /**
@@ -236,8 +251,13 @@
             return;
         }
 
+        if (mPath == null) {
+            mPath = new Path();
+        } else {
+            mPath.rewind();
+        }
+
         mMode = MODE_CONVEX_PATH;
-        mPath.rewind();
         mPath.addOval(left, top, right, bottom, Path.Direction.CW);
         mRect.setEmpty();
         mRadius = RADIUS_UNDEFINED;
@@ -264,6 +284,10 @@
             throw new IllegalArgumentException("path must be convex");
         }
 
+        if (mPath == null) {
+            mPath = new Path();
+        }
+
         mMode = MODE_CONVEX_PATH;
         mPath.set(convexPath);
         mRect.setEmpty();
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 554e5d2..fc873c4 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -61,7 +61,6 @@
     private ColorFilter mColorFilter;
     private MaskFilter  mMaskFilter;
     private PathEffect  mPathEffect;
-    private Rasterizer  mRasterizer;
     private Shader      mShader;
     private Typeface    mTypeface;
     private Xfermode    mXfermode;
@@ -494,7 +493,6 @@
         mColorFilter = null;
         mMaskFilter = null;
         mPathEffect = null;
-        mRasterizer = null;
         mShader = null;
         mNativeShader = 0;
         mTypeface = null;
@@ -532,7 +530,6 @@
         mColorFilter = paint.mColorFilter;
         mMaskFilter = paint.mMaskFilter;
         mPathEffect = paint.mPathEffect;
-        mRasterizer = paint.mRasterizer;
         mShader = paint.mShader;
         mNativeShader = paint.mNativeShader;
         mTypeface = paint.mTypeface;
@@ -1164,11 +1161,12 @@
      *
      * @return         the paint's rasterizer (or null)
      *
-     *  @deprecated Rasterizer is not supported by either the HW or PDF backends.
+     * @deprecated Rasterizer is not supported by either the HW or PDF backends.
+     * @removed
      */
     @Deprecated
     public Rasterizer getRasterizer() {
-        return mRasterizer;
+        return null;
     }
 
     /**
@@ -1181,16 +1179,11 @@
      *                   the paint.
      * @return           rasterizer
      *
-     *  @deprecated Rasterizer is not supported by either the HW or PDF backends.
+     * @deprecated Rasterizer is not supported by either the HW or PDF backends.
+     * @removed
      */
     @Deprecated
     public Rasterizer setRasterizer(Rasterizer rasterizer) {
-        long rasterizerNative = 0;
-        if (rasterizer != null) {
-            rasterizerNative = rasterizer.native_instance;
-        }
-        nSetRasterizer(mNativePaint, rasterizerNative);
-        mRasterizer = rasterizer;
         return rasterizer;
     }
 
@@ -2715,8 +2708,6 @@
     @CriticalNative
     private static native long nSetTypeface(long paintPtr, long typeface);
     @CriticalNative
-    private static native long nSetRasterizer(long paintPtr, long rasterizer);
-    @CriticalNative
     private static native int nGetTextAlign(long paintPtr);
     @CriticalNative
     private static native void nSetTextAlign(long paintPtr, int align);
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index 4ed2581..3631373 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -19,6 +19,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
 /**
  * The Path class encapsulates compound (multiple contour) geometric paths
  * consisting of straight line segments, quadratic curves, and cubic curves.
@@ -46,7 +49,7 @@
      * Create an empty path
      */
     public Path() {
-        mNativePath = init1();
+        mNativePath = nInit();
     }
 
     /**
@@ -63,7 +66,7 @@
                 rects = new Region(src.rects);
             }
         }
-        mNativePath = init2(valNative);
+        mNativePath = nInit(valNative);
     }
 
     /**
@@ -77,7 +80,7 @@
         // We promised not to change this, so preserve it around the native
         // call, which does now reset fill type.
         final FillType fillType = getFillType();
-        native_reset(mNativePath);
+        nReset(mNativePath);
         setFillType(fillType);
     }
 
@@ -89,7 +92,7 @@
         isSimplePath = true;
         mLastDirection = null;
         if (rects != null) rects.setEmpty();
-        native_rewind(mNativePath);
+        nRewind(mNativePath);
     }
 
     /** Replace the contents of this with the contents of src.
@@ -99,7 +102,7 @@
             return;
         }
         isSimplePath = src.isSimplePath;
-        native_set(mNativePath, src.mNativePath);
+        nSet(mNativePath, src.mNativePath);
         if (!isSimplePath) {
             return;
         }
@@ -174,7 +177,7 @@
      * @see #op(Path, android.graphics.Path.Op)
      */
     public boolean op(Path path1, Path path2, Op op) {
-        if (native_op(path1.mNativePath, path2.mNativePath, op.ordinal(), this.mNativePath)) {
+        if (nOp(path1.mNativePath, path2.mNativePath, op.ordinal(), this.mNativePath)) {
             isSimplePath = false;
             rects = null;
             return true;
@@ -194,7 +197,7 @@
      * @return True if the path is convex.
      */
     public boolean isConvex() {
-        return native_isConvex(mNativePath);
+        return nIsConvex(mNativePath);
     }
 
     /**
@@ -243,7 +246,7 @@
      * @return the path's fill type
      */
     public FillType getFillType() {
-        return sFillTypeArray[native_getFillType(mNativePath)];
+        return sFillTypeArray[nGetFillType(mNativePath)];
     }
 
     /**
@@ -252,7 +255,7 @@
      * @param ft The new fill type for this path
      */
     public void setFillType(FillType ft) {
-        native_setFillType(mNativePath, ft.nativeInt);
+        nSetFillType(mNativePath, ft.nativeInt);
     }
 
     /**
@@ -261,7 +264,7 @@
      * @return true if the filltype is one of the INVERSE variants
      */
     public boolean isInverseFillType() {
-        final int ft = native_getFillType(mNativePath);
+        final int ft = nGetFillType(mNativePath);
         return (ft & FillType.INVERSE_WINDING.nativeInt) != 0;
     }
 
@@ -269,9 +272,9 @@
      * Toggles the INVERSE state of the filltype
      */
     public void toggleInverseFillType() {
-        int ft = native_getFillType(mNativePath);
+        int ft = nGetFillType(mNativePath);
         ft ^= FillType.INVERSE_WINDING.nativeInt;
-        native_setFillType(mNativePath, ft);
+        nSetFillType(mNativePath, ft);
     }
 
     /**
@@ -280,7 +283,7 @@
      * @return true if the path is empty (contains no lines or curves)
      */
     public boolean isEmpty() {
-        return native_isEmpty(mNativePath);
+        return nIsEmpty(mNativePath);
     }
 
     /**
@@ -293,7 +296,7 @@
      * @return     true if the path specifies a rectangle
      */
     public boolean isRect(RectF rect) {
-        return native_isRect(mNativePath, rect);
+        return nIsRect(mNativePath, rect);
     }
 
     /**
@@ -306,7 +309,7 @@
      */
     @SuppressWarnings({"UnusedDeclaration"})
     public void computeBounds(RectF bounds, boolean exact) {
-        native_computeBounds(mNativePath, bounds);
+        nComputeBounds(mNativePath, bounds);
     }
 
     /**
@@ -317,7 +320,7 @@
      *                     path
      */
     public void incReserve(int extraPtCount) {
-        native_incReserve(mNativePath, extraPtCount);
+        nIncReserve(mNativePath, extraPtCount);
     }
 
     /**
@@ -327,7 +330,7 @@
      * @param y The y-coordinate of the start of a new contour
      */
     public void moveTo(float x, float y) {
-        native_moveTo(mNativePath, x, y);
+        nMoveTo(mNativePath, x, y);
     }
 
     /**
@@ -341,7 +344,7 @@
      *           previous contour, to specify the start of a new contour
      */
     public void rMoveTo(float dx, float dy) {
-        native_rMoveTo(mNativePath, dx, dy);
+        nRMoveTo(mNativePath, dx, dy);
     }
 
     /**
@@ -354,7 +357,7 @@
      */
     public void lineTo(float x, float y) {
         isSimplePath = false;
-        native_lineTo(mNativePath, x, y);
+        nLineTo(mNativePath, x, y);
     }
 
     /**
@@ -369,7 +372,7 @@
      */
     public void rLineTo(float dx, float dy) {
         isSimplePath = false;
-        native_rLineTo(mNativePath, dx, dy);
+        nRLineTo(mNativePath, dx, dy);
     }
 
     /**
@@ -384,7 +387,7 @@
      */
     public void quadTo(float x1, float y1, float x2, float y2) {
         isSimplePath = false;
-        native_quadTo(mNativePath, x1, y1, x2, y2);
+        nQuadTo(mNativePath, x1, y1, x2, y2);
     }
 
     /**
@@ -403,7 +406,7 @@
      */
     public void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
         isSimplePath = false;
-        native_rQuadTo(mNativePath, dx1, dy1, dx2, dy2);
+        nRQuadTo(mNativePath, dx1, dy1, dx2, dy2);
     }
 
     /**
@@ -421,7 +424,7 @@
     public void cubicTo(float x1, float y1, float x2, float y2,
                         float x3, float y3) {
         isSimplePath = false;
-        native_cubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
+        nCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
     }
 
     /**
@@ -432,7 +435,7 @@
     public void rCubicTo(float x1, float y1, float x2, float y2,
                          float x3, float y3) {
         isSimplePath = false;
-        native_rCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
+        nRCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
     }
 
     /**
@@ -483,7 +486,7 @@
     public void arcTo(float left, float top, float right, float bottom, float startAngle,
             float sweepAngle, boolean forceMoveTo) {
         isSimplePath = false;
-        native_arcTo(mNativePath, left, top, right, bottom, startAngle, sweepAngle, forceMoveTo);
+        nArcTo(mNativePath, left, top, right, bottom, startAngle, sweepAngle, forceMoveTo);
     }
 
     /**
@@ -492,7 +495,7 @@
      */
     public void close() {
         isSimplePath = false;
-        native_close(mNativePath);
+        nClose(mNativePath);
     }
 
     /**
@@ -544,7 +547,7 @@
      */
     public void addRect(float left, float top, float right, float bottom, Direction dir) {
         detectSimplePath(left, top, right, bottom, dir);
-        native_addRect(mNativePath, left, top, right, bottom, dir.nativeInt);
+        nAddRect(mNativePath, left, top, right, bottom, dir.nativeInt);
     }
 
     /**
@@ -564,7 +567,7 @@
      */
     public void addOval(float left, float top, float right, float bottom, Direction dir) {
         isSimplePath = false;
-        native_addOval(mNativePath, left, top, right, bottom, dir.nativeInt);
+        nAddOval(mNativePath, left, top, right, bottom, dir.nativeInt);
     }
 
     /**
@@ -577,7 +580,7 @@
      */
     public void addCircle(float x, float y, float radius, Direction dir) {
         isSimplePath = false;
-        native_addCircle(mNativePath, x, y, radius, dir.nativeInt);
+        nAddCircle(mNativePath, x, y, radius, dir.nativeInt);
     }
 
     /**
@@ -600,7 +603,7 @@
     public void addArc(float left, float top, float right, float bottom, float startAngle,
             float sweepAngle) {
         isSimplePath = false;
-        native_addArc(mNativePath, left, top, right, bottom, startAngle, sweepAngle);
+        nAddArc(mNativePath, left, top, right, bottom, startAngle, sweepAngle);
     }
 
     /**
@@ -625,7 +628,7 @@
     public void addRoundRect(float left, float top, float right, float bottom, float rx, float ry,
             Direction dir) {
         isSimplePath = false;
-        native_addRoundRect(mNativePath, left, top, right, bottom, rx, ry, dir.nativeInt);
+        nAddRoundRect(mNativePath, left, top, right, bottom, rx, ry, dir.nativeInt);
     }
 
     /**
@@ -658,7 +661,7 @@
             throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
         }
         isSimplePath = false;
-        native_addRoundRect(mNativePath, left, top, right, bottom, radii, dir.nativeInt);
+        nAddRoundRect(mNativePath, left, top, right, bottom, radii, dir.nativeInt);
     }
 
     /**
@@ -669,7 +672,7 @@
      */
     public void addPath(Path src, float dx, float dy) {
         isSimplePath = false;
-        native_addPath(mNativePath, src.mNativePath, dx, dy);
+        nAddPath(mNativePath, src.mNativePath, dx, dy);
     }
 
     /**
@@ -679,7 +682,7 @@
      */
     public void addPath(Path src) {
         isSimplePath = false;
-        native_addPath(mNativePath, src.mNativePath);
+        nAddPath(mNativePath, src.mNativePath);
     }
 
     /**
@@ -689,7 +692,7 @@
      */
     public void addPath(Path src, Matrix matrix) {
         if (!src.isSimplePath) isSimplePath = false;
-        native_addPath(mNativePath, src.mNativePath, matrix.native_instance);
+        nAddPath(mNativePath, src.mNativePath, matrix.native_instance);
     }
 
     /**
@@ -725,7 +728,7 @@
         } else {
             isSimplePath = false;
         }
-        native_offset(mNativePath, dx, dy);
+        nOffset(mNativePath, dx, dy);
     }
 
     /**
@@ -736,7 +739,7 @@
      */
     public void setLastPoint(float dx, float dy) {
         isSimplePath = false;
-        native_setLastPoint(mNativePath, dx, dy);
+        nSetLastPoint(mNativePath, dx, dy);
     }
 
     /**
@@ -753,7 +756,7 @@
             dst.isSimplePath = false;
             dstNative = dst.mNativePath;
         }
-        native_transform(mNativePath, matrix.native_instance, dstNative);
+        nTransform(mNativePath, matrix.native_instance, dstNative);
     }
 
     /**
@@ -763,12 +766,12 @@
      */
     public void transform(Matrix matrix) {
         isSimplePath = false;
-        native_transform(mNativePath, matrix.native_instance);
+        nTransform(mNativePath, matrix.native_instance);
     }
 
     protected void finalize() throws Throwable {
         try {
-            finalizer(mNativePath);
+            nFinalize(mNativePath);
             mNativePath = 0;  //  Other finalizers can still call us.
         } finally {
             super.finalize();
@@ -803,59 +806,68 @@
      * @return An array of components for points approximating the Path.
      */
     public float[] approximate(float acceptableError) {
-        return native_approximate(mNativePath, acceptableError);
+        return nApproximate(mNativePath, acceptableError);
     }
 
-    private static native long init1();
-    private static native long init2(long nPath);
-    private static native void native_reset(long nPath);
-    private static native void native_rewind(long nPath);
-    private static native void native_set(long native_dst, long native_src);
-    private static native boolean native_isConvex(long nPath);
-    private static native int native_getFillType(long nPath);
-    private static native void native_setFillType(long nPath, int ft);
-    private static native boolean native_isEmpty(long nPath);
-    private static native boolean native_isRect(long nPath, RectF rect);
-    private static native void native_computeBounds(long nPath, RectF bounds);
-    private static native void native_incReserve(long nPath, int extraPtCount);
-    private static native void native_moveTo(long nPath, float x, float y);
-    private static native void native_rMoveTo(long nPath, float dx, float dy);
-    private static native void native_lineTo(long nPath, float x, float y);
-    private static native void native_rLineTo(long nPath, float dx, float dy);
-    private static native void native_quadTo(long nPath, float x1, float y1,
-                                             float x2, float y2);
-    private static native void native_rQuadTo(long nPath, float dx1, float dy1,
-                                              float dx2, float dy2);
-    private static native void native_cubicTo(long nPath, float x1, float y1,
-                                        float x2, float y2, float x3, float y3);
-    private static native void native_rCubicTo(long nPath, float x1, float y1,
-                                        float x2, float y2, float x3, float y3);
-    private static native void native_arcTo(long nPath, float left, float top,
-                                            float right, float bottom, float startAngle,
-                                            float sweepAngle, boolean forceMoveTo);
-    private static native void native_close(long nPath);
-    private static native void native_addRect(long nPath, float left, float top,
-                                            float right, float bottom, int dir);
-    private static native void native_addOval(long nPath, float left, float top,
+    // ------------------ Regular JNI ------------------------
+
+    private static native long nInit();
+    private static native long nInit(long nPath);
+    private static native void nFinalize(long nPath);
+    private static native void nSet(long native_dst, long nSrc);
+    private static native void nComputeBounds(long nPath, RectF bounds);
+    private static native void nIncReserve(long nPath, int extraPtCount);
+    private static native void nMoveTo(long nPath, float x, float y);
+    private static native void nRMoveTo(long nPath, float dx, float dy);
+    private static native void nLineTo(long nPath, float x, float y);
+    private static native void nRLineTo(long nPath, float dx, float dy);
+    private static native void nQuadTo(long nPath, float x1, float y1, float x2, float y2);
+    private static native void nRQuadTo(long nPath, float dx1, float dy1, float dx2, float dy2);
+    private static native void nCubicTo(long nPath, float x1, float y1, float x2, float y2,
+            float x3, float y3);
+    private static native void nRCubicTo(long nPath, float x1, float y1, float x2, float y2,
+            float x3, float y3);
+    private static native void nArcTo(long nPath, float left, float top, float right, float bottom,
+            float startAngle, float sweepAngle, boolean forceMoveTo);
+    private static native void nClose(long nPath);
+    private static native void nAddRect(long nPath, float left, float top,
             float right, float bottom, int dir);
-    private static native void native_addCircle(long nPath, float x, float y, float radius, int dir);
-    private static native void native_addArc(long nPath, float left, float top,
-                                             float right, float bottom,
-                                             float startAngle, float sweepAngle);
-    private static native void native_addRoundRect(long nPath, float left, float top,
-                                                   float right, float bottom,
-                                                   float rx, float ry, int dir);
-    private static native void native_addRoundRect(long nPath, float left, float top,
-                                                   float right, float bottom,
-                                                   float[] radii, int dir);
-    private static native void native_addPath(long nPath, long src, float dx, float dy);
-    private static native void native_addPath(long nPath, long src);
-    private static native void native_addPath(long nPath, long src, long matrix);
-    private static native void native_offset(long nPath, float dx, float dy);
-    private static native void native_setLastPoint(long nPath, float dx, float dy);
-    private static native void native_transform(long nPath, long matrix, long dst_path);
-    private static native void native_transform(long nPath, long matrix);
-    private static native boolean native_op(long path1, long path2, int op, long result);
-    private static native void finalizer(long nPath);
-    private static native float[] native_approximate(long nPath, float error);
+    private static native void nAddOval(long nPath, float left, float top,
+            float right, float bottom, int dir);
+    private static native void nAddCircle(long nPath, float x, float y, float radius, int dir);
+    private static native void nAddArc(long nPath, float left, float top, float right, float bottom,
+            float startAngle, float sweepAngle);
+    private static native void nAddRoundRect(long nPath, float left, float top,
+            float right, float bottom, float rx, float ry, int dir);
+    private static native void nAddRoundRect(long nPath, float left, float top,
+            float right, float bottom, float[] radii, int dir);
+    private static native void nAddPath(long nPath, long src, float dx, float dy);
+    private static native void nAddPath(long nPath, long src);
+    private static native void nAddPath(long nPath, long src, long matrix);
+    private static native void nOffset(long nPath, float dx, float dy);
+    private static native void nSetLastPoint(long nPath, float dx, float dy);
+    private static native void nTransform(long nPath, long matrix, long dst_path);
+    private static native void nTransform(long nPath, long matrix);
+    private static native boolean nOp(long path1, long path2, int op, long result);
+    private static native float[] nApproximate(long nPath, float error);
+
+    // ------------------ Fast JNI ------------------------
+
+    @FastNative
+    private static native boolean nIsRect(long nPath, RectF rect);
+
+    // ------------------ Critical JNI ------------------------
+
+    @CriticalNative
+    private static native void nReset(long nPath);
+    @CriticalNative
+    private static native void nRewind(long nPath);
+    @CriticalNative
+    private static native boolean nIsEmpty(long nPath);
+    @CriticalNative
+    private static native boolean nIsConvex(long nPath);
+    @CriticalNative
+    private static native int nGetFillType(long nPath);
+    @CriticalNative
+    private static native void nSetFillType(long nPath, int ft);
 }
diff --git a/graphics/java/android/graphics/Rasterizer.java b/graphics/java/android/graphics/Rasterizer.java
index f6a38fe..29d82fa 100644
--- a/graphics/java/android/graphics/Rasterizer.java
+++ b/graphics/java/android/graphics/Rasterizer.java
@@ -21,15 +21,10 @@
 
 package android.graphics;
 
-@Deprecated
+/**
+ * @removed feature is not supported by hw-accerlerated or PDF backends
+ */
 public class Rasterizer {
 
-    protected void finalize() throws Throwable {
-        finalizer(native_instance);
-        native_instance = 0;
-    }
-
-    private static native void finalizer(long native_instance);
-
-    long native_instance;
+    protected void finalize() throws Throwable { }
 }
diff --git a/legacy-test/Android.mk b/legacy-test/Android.mk
index 7bab189..5e72a0d 100644
--- a/legacy-test/Android.mk
+++ b/legacy-test/Android.mk
@@ -25,7 +25,6 @@
 LOCAL_MODULE := legacy-test
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
-LOCAL_STATIC_JAVA_LIBRARIES := core-junit-static
 
 include $(BUILD_JAVA_LIBRARY)
 
diff --git a/legacy-test/src/junit/MODULE_LICENSE_CPL b/legacy-test/src/junit/MODULE_LICENSE_CPL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/legacy-test/src/junit/MODULE_LICENSE_CPL
diff --git a/legacy-test/src/junit/README.android b/legacy-test/src/junit/README.android
new file mode 100644
index 0000000..1384a1f
--- /dev/null
+++ b/legacy-test/src/junit/README.android
@@ -0,0 +1,11 @@
+URL: https://github.com/junit-team/junit4
+License: Common Public License Version 1.0
+License File: cpl-v10.html
+
+This is JUnit 4.10 source that was previously part of the Android Public API.
+Where necessary it has been patched to be compatible (according to Android API
+requirements) with JUnit 3.8.
+
+These are copied here to ensure that the android.test.runner target remains
+compatible with the last version of the Android API (25) that contained these
+classes even when external/junit is upgraded to a later version.
diff --git a/legacy-test/src/junit/cpl-v10.html b/legacy-test/src/junit/cpl-v10.html
new file mode 100644
index 0000000..36aa208
--- /dev/null
+++ b/legacy-test/src/junit/cpl-v10.html
@@ -0,0 +1,125 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<HTML>
+<HEAD>
+<TITLE>Common Public License - v 1.0</TITLE>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</HEAD>
+
+<BODY BGCOLOR="#FFFFFF" VLINK="#800000">
+
+
+<P ALIGN="CENTER"><B>Common Public License - v 1.0</B>
+<P><B></B><FONT SIZE="3"></FONT>
+<P><FONT SIZE="3"></FONT><FONT SIZE="2">THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT").  ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.</FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"><B>1.  DEFINITIONS</B></FONT>
+<P><FONT SIZE="2">"Contribution" means:</FONT>
+
+<UL><FONT SIZE="2">a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and<BR CLEAR="LEFT">
+b) in the case of each subsequent Contributor:</FONT></UL>
+
+
+<UL><FONT SIZE="2">i)	 	changes to the Program, and</FONT></UL>
+
+
+<UL><FONT SIZE="2">ii)		additions to the Program;</FONT></UL>
+
+
+<UL><FONT SIZE="2">where such changes and/or additions to the Program originate from and are distributed by that particular Contributor.  </FONT><FONT SIZE="2">A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf.  </FONT><FONT SIZE="2">Contributions do not include additions to the Program which:  (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.  </FONT></UL>
+
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">"Contributor" means any person or entity that distributes the Program.</FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.  </FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2">"Program" means the Contributions distributed in accordance with this Agreement.</FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.</FONT>
+<P><FONT SIZE="2"><B></B></FONT>
+<P><FONT SIZE="2"><B>2.  GRANT OF RIGHTS</B></FONT>
+
+<UL><FONT SIZE="2"></FONT><FONT SIZE="2">a)	</FONT><FONT SIZE="2">Subject to the terms of this Agreement, each Contributor hereby grants</FONT><FONT SIZE="2"> Recipient a non-exclusive, worldwide, royalty-free copyright license to</FONT><FONT SIZE="2" COLOR="#FF0000"> </FONT><FONT SIZE="2">reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.</FONT></UL>
+
+
+<UL><FONT SIZE="2"></FONT></UL>
+
+
+<UL><FONT SIZE="2"></FONT><FONT SIZE="2">b) 	Subject to the terms of this Agreement, each Contributor hereby grants </FONT><FONT SIZE="2">Recipient a non-exclusive, worldwide,</FONT><FONT SIZE="2" COLOR="#008000"> </FONT><FONT SIZE="2">royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form.  This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents.  The patent license shall not apply to any other combinations which include the Contribution.  No hardware per se is licensed hereunder.   </FONT></UL>
+
+
+<UL><FONT SIZE="2"></FONT></UL>
+
+
+<UL><FONT SIZE="2">c)	Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity.  Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise.  As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any.  For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.</FONT></UL>
+
+
+<UL><FONT SIZE="2"></FONT></UL>
+
+
+<UL><FONT SIZE="2">d)	Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. </FONT></UL>
+
+
+<UL><FONT SIZE="2"></FONT></UL>
+
+<P><FONT SIZE="2"><B>3.  REQUIREMENTS</B></FONT>
+<P><FONT SIZE="2"><B></B>A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:</FONT>
+
+<UL><FONT SIZE="2">a)	it complies with the terms and conditions of this Agreement; and</FONT></UL>
+
+
+<UL><FONT SIZE="2">b)	its license agreement:</FONT></UL>
+
+
+<UL><FONT SIZE="2">i)	effectively disclaims</FONT><FONT SIZE="2"> on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; </FONT></UL>
+
+
+<UL><FONT SIZE="2">ii) 	effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; </FONT></UL>
+
+
+<UL><FONT SIZE="2">iii)</FONT><FONT SIZE="2">	states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and</FONT></UL>
+
+
+<UL><FONT SIZE="2">iv)	states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.</FONT><FONT SIZE="2" COLOR="#0000FF"> </FONT><FONT SIZE="2" COLOR="#FF0000"></FONT></UL>
+
+
+<UL><FONT SIZE="2" COLOR="#FF0000"></FONT><FONT SIZE="2"></FONT></UL>
+
+<P><FONT SIZE="2">When the Program is made available in source code form:</FONT>
+
+<UL><FONT SIZE="2">a)	it must be made available under this Agreement; and </FONT></UL>
+
+
+<UL><FONT SIZE="2">b)	a copy of this Agreement must be included with each copy of the Program.  </FONT></UL>
+
+<P><FONT SIZE="2"></FONT><FONT SIZE="2" COLOR="#0000FF"><STRIKE></STRIKE></FONT>
+<P><FONT SIZE="2" COLOR="#0000FF"><STRIKE></STRIKE></FONT><FONT SIZE="2">Contributors may not remove or alter any copyright notices contained within the Program.  </FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.  </FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"><B>4.  COMMERCIAL DISTRIBUTION</B></FONT>
+<P><FONT SIZE="2">Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like.  While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors.   Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering.  The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement.  In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations.  The Indemnified Contributor may participate in any such claim at its own expense.</FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">For example, a Contributor might include the Program in a commercial product offering, Product X.  That Contributor is then a Commercial Contributor.  If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone.  Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.</FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2" COLOR="#0000FF"></FONT>
+<P><FONT SIZE="2" COLOR="#0000FF"></FONT><FONT SIZE="2"><B>5.  NO WARRANTY</B></FONT>
+<P><FONT SIZE="2">EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is</FONT><FONT SIZE="2"> solely responsible for determining the appropriateness of using and distributing </FONT><FONT SIZE="2">the Program</FONT><FONT SIZE="2"> and assumes all risks associated with its exercise of rights under this Agreement</FONT><FONT SIZE="2">, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, </FONT><FONT SIZE="2">programs or equipment, and unavailability or interruption of operations</FONT><FONT SIZE="2">.  </FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2"><B>6.  DISCLAIMER OF LIABILITY</B></FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2">EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES </FONT><FONT SIZE="2">(INCLUDING WITHOUT LIMITATION LOST PROFITS),</FONT><FONT SIZE="2"> HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"><B>7.  GENERAL</B></FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2">If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.</FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed.  In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. </FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance.  If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable.  However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.  </FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2">Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted  and may only be modified in the following manner. The Agreement Steward reserves the right to </FONT><FONT SIZE="2">publish new versions (including revisions) of this Agreement from time to </FONT><FONT SIZE="2">time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward.   IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity.  </FONT><FONT SIZE="2">Each new version of the Agreement will be given a distinguishing version number.  The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new </FONT><FONT SIZE="2">version.  </FONT><FONT SIZE="2">Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, </FONT><FONT SIZE="2">by implication, estoppel or otherwise</FONT><FONT SIZE="2">.</FONT><FONT SIZE="2">  All rights in the Program not expressly granted under this Agreement are reserved.</FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose.  Each party waives its rights to a jury trial in any resulting litigation.</FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT>
+
+</BODY>
+
+</HTML>
\ No newline at end of file
diff --git a/legacy-test/src/junit/framework/Assert.java b/legacy-test/src/junit/framework/Assert.java
new file mode 100644
index 0000000..3dcc23d
--- /dev/null
+++ b/legacy-test/src/junit/framework/Assert.java
@@ -0,0 +1,296 @@
+package junit.framework;
+
+/**
+ * A set of assert methods.  Messages are only displayed when an assert fails.
+ */
+
+public class Assert {
+	/**
+	 * Protect constructor since it is a static only class
+	 */
+	protected Assert() {
+	}
+
+	/**
+	 * Asserts that a condition is true. If it isn't it throws
+	 * an AssertionFailedError with the given message.
+	 */
+	static public void assertTrue(String message, boolean condition) {
+		if (!condition)
+			fail(message);
+	}
+	/**
+	 * Asserts that a condition is true. If it isn't it throws
+	 * an AssertionFailedError.
+	 */
+	static public void assertTrue(boolean condition) {
+		assertTrue(null, condition);
+	}
+	/**
+	 * Asserts that a condition is false. If it isn't it throws
+	 * an AssertionFailedError with the given message.
+	 */
+	static public void assertFalse(String message, boolean condition) {
+		assertTrue(message, !condition);
+	}
+	/**
+	 * Asserts that a condition is false. If it isn't it throws
+	 * an AssertionFailedError.
+	 */
+	static public void assertFalse(boolean condition) {
+		assertFalse(null, condition);
+	}
+	/**
+	 * Fails a test with the given message.
+	 */
+	static public void fail(String message) {
+		if (message == null) {
+			throw new AssertionFailedError();
+		}
+		throw new AssertionFailedError(message);
+	}
+	/**
+	 * Fails a test with no message.
+	 */
+	static public void fail() {
+		fail(null);
+	}
+	/**
+	 * Asserts that two objects are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, Object expected, Object actual) {
+		if (expected == null && actual == null)
+			return;
+		if (expected != null && expected.equals(actual))
+			return;
+		failNotEquals(message, expected, actual);
+	}
+	/**
+	 * Asserts that two objects are equal. If they are not
+	 * an AssertionFailedError is thrown.
+	 */
+	static public void assertEquals(Object expected, Object actual) {
+	    assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two Strings are equal. 
+	 */
+	static public void assertEquals(String message, String expected, String actual) {
+		if (expected == null && actual == null)
+			return;
+		if (expected != null && expected.equals(actual))
+			return;
+		String cleanMessage= message == null ? "" : message;
+		throw new ComparisonFailure(cleanMessage, expected, actual);
+	}
+	/**
+	 * Asserts that two Strings are equal. 
+	 */
+	static public void assertEquals(String expected, String actual) {
+	    assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two doubles are equal concerning a delta.  If they are not
+	 * an AssertionFailedError is thrown with the given message.  If the expected
+	 * value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(String message, double expected, double actual, double delta) {
+		if (Double.compare(expected, actual) == 0)
+			return;
+		if (!(Math.abs(expected-actual) <= delta))
+			failNotEquals(message, new Double(expected), new Double(actual));
+	}
+	/**
+	 * Asserts that two doubles are equal concerning a delta. If the expected
+	 * value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(double expected, double actual, double delta) {
+	    assertEquals(null, expected, actual, delta);
+	}
+	/**
+	 * Asserts that two floats are equal concerning a positive delta. If they
+	 * are not an AssertionFailedError is thrown with the given message. If the
+	 * expected value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(String message, float expected, float actual, float delta) {
+		if (Float.compare(expected, actual) == 0)
+			return;
+		if (!(Math.abs(expected - actual) <= delta))
+				failNotEquals(message, new Float(expected), new Float(actual));
+	}
+	/**
+	 * Asserts that two floats are equal concerning a delta. If the expected
+	 * value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(float expected, float actual, float delta) {
+		assertEquals(null, expected, actual, delta);
+	}
+	/**
+	 * Asserts that two longs are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, long expected, long actual) {
+	    assertEquals(message, new Long(expected), new Long(actual));
+	}
+	/**
+	 * Asserts that two longs are equal.
+	 */
+	static public void assertEquals(long expected, long actual) {
+	    assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two booleans are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, boolean expected, boolean actual) {
+    		assertEquals(message, Boolean.valueOf(expected), Boolean.valueOf(actual));
+  	}
+	/**
+	 * Asserts that two booleans are equal.
+ 	 */
+	static public void assertEquals(boolean expected, boolean actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two bytes are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+  	static public void assertEquals(String message, byte expected, byte actual) {
+		assertEquals(message, new Byte(expected), new Byte(actual));
+	}
+	/**
+   	 * Asserts that two bytes are equal.
+	 */
+	static public void assertEquals(byte expected, byte actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two chars are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+  	static public void assertEquals(String message, char expected, char actual) {
+    		assertEquals(message, new Character(expected), new Character(actual));
+  	}
+	/**
+	 * Asserts that two chars are equal.
+	 */
+  	static public void assertEquals(char expected, char actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two shorts are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, short expected, short actual) {
+    		assertEquals(message, new Short(expected), new Short(actual));
+	}
+  	/**
+	 * Asserts that two shorts are equal.
+	 */
+	static public void assertEquals(short expected, short actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two ints are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+  	static public void assertEquals(String message, int expected, int actual) {
+		assertEquals(message, new Integer(expected), new Integer(actual));
+  	}
+  	/**
+   	 * Asserts that two ints are equal.
+	 */
+  	static public void assertEquals(int expected, int actual) {
+  		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that an object isn't null.
+	 */
+	static public void assertNotNull(Object object) {
+		assertNotNull(null, object);
+	}
+	/**
+	 * Asserts that an object isn't null. If it is
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertNotNull(String message, Object object) {
+		assertTrue(message, object != null);
+	}
+	/**
+	 * Asserts that an object is null. If it isn't an {@link AssertionError} is
+	 * thrown.
+	 * Message contains: Expected: <null> but was: object
+	 * 
+	 * @param object
+	 *            Object to check or <code>null</code>
+	 */
+	static public void assertNull(Object object) {
+		String message = "Expected: <null> but was: " + String.valueOf(object);
+		assertNull(message, object);
+	}
+	/**
+	 * Asserts that an object is null.  If it is not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertNull(String message, Object object) {
+		assertTrue(message, object == null);
+	}
+	/**
+	 * Asserts that two objects refer to the same object. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertSame(String message, Object expected, Object actual) {
+		if (expected == actual)
+			return;
+		failNotSame(message, expected, actual);
+	}
+	/**
+	 * Asserts that two objects refer to the same object. If they are not
+	 * the same an AssertionFailedError is thrown.
+	 */
+	static public void assertSame(Object expected, Object actual) {
+	    assertSame(null, expected, actual);
+	}
+	/**
+	 * Asserts that two objects do not refer to the same object. If they do
+	 * refer to the same object an AssertionFailedError is thrown with the
+	 * given message.
+	 */
+	static public void assertNotSame(String message, Object expected, Object actual) {
+		if (expected == actual)
+			failSame(message);
+	}
+	/**
+	 * Asserts that two objects do not refer to the same object. If they do
+	 * refer to the same object an AssertionFailedError is thrown.
+	 */
+	static public void assertNotSame(Object expected, Object actual) {
+		assertNotSame(null, expected, actual);
+	}
+
+	static public void failSame(String message) {
+		String formatted= "";
+ 		if (message != null)
+ 			formatted= message+" ";
+ 		fail(formatted+"expected not same");
+	}
+
+	static public void failNotSame(String message, Object expected, Object actual) {
+		String formatted= "";
+		if (message != null)
+			formatted= message+" ";
+		fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
+	}
+
+	static public void failNotEquals(String message, Object expected, Object actual) {
+		fail(format(message, expected, actual));
+	}
+
+	public static String format(String message, Object expected, Object actual) {
+		String formatted= "";
+		if (message != null && message.length() > 0)
+			formatted= message+" ";
+		return formatted+"expected:<"+expected+"> but was:<"+actual+">";
+	}
+}
diff --git a/legacy-test/src/junit/framework/AssertionFailedError.java b/legacy-test/src/junit/framework/AssertionFailedError.java
new file mode 100644
index 0000000..0d7802c
--- /dev/null
+++ b/legacy-test/src/junit/framework/AssertionFailedError.java
@@ -0,0 +1,20 @@
+package junit.framework;
+
+/**
+ * Thrown when an assertion failed.
+ */
+public class AssertionFailedError extends AssertionError {
+
+	private static final long serialVersionUID= 1L;
+
+	public AssertionFailedError() {
+	}
+
+	public AssertionFailedError(String message) {
+		super(defaultString(message));
+	}
+
+	private static String defaultString(String message) {
+		return message == null ? "" : message;
+	}
+}
\ No newline at end of file
diff --git a/legacy-test/src/junit/framework/ComparisonCompactor.java b/legacy-test/src/junit/framework/ComparisonCompactor.java
new file mode 100644
index 0000000..e540f03
--- /dev/null
+++ b/legacy-test/src/junit/framework/ComparisonCompactor.java
@@ -0,0 +1,87 @@
+package junit.framework;
+
+// android-changed add @hide
+/**
+ * @hide not needed for public API
+ */
+public class ComparisonCompactor {
+
+	private static final String ELLIPSIS= "...";
+	private static final String DELTA_END= "]";
+	private static final String DELTA_START= "[";
+	
+	private int fContextLength;
+	private String fExpected;
+	private String fActual;
+	private int fPrefix;
+	private int fSuffix;
+
+	public ComparisonCompactor(int contextLength, String expected, String actual) {
+		fContextLength= contextLength;
+		fExpected= expected;
+		fActual= actual;
+	}
+
+	public String compact(String message) {
+		if (fExpected == null || fActual == null || areStringsEqual()) {
+			// android-changed use local method instead of Assert.format, since
+			// the later is not part of Android API till API 16
+			return format(message, fExpected, fActual);
+		}
+		findCommonPrefix();
+		findCommonSuffix();
+		String expected= compactString(fExpected);
+		String actual= compactString(fActual);
+		// android-changed use local format method
+		return format(message, expected, actual);
+	}
+
+	private String compactString(String source) {
+		String result= DELTA_START + source.substring(fPrefix, source.length() - fSuffix + 1) + DELTA_END;
+		if (fPrefix > 0)
+			result= computeCommonPrefix() + result;
+		if (fSuffix > 0)
+			result= result + computeCommonSuffix();
+		return result;
+	}
+
+	private void findCommonPrefix() {
+		fPrefix= 0;
+		int end= Math.min(fExpected.length(), fActual.length());
+		for (; fPrefix < end; fPrefix++) {
+			if (fExpected.charAt(fPrefix) != fActual.charAt(fPrefix))
+				break;
+		}
+	}
+
+	private void findCommonSuffix() {
+		int expectedSuffix= fExpected.length() - 1;
+		int actualSuffix= fActual.length() - 1;
+		for (; actualSuffix >= fPrefix && expectedSuffix >= fPrefix; actualSuffix--, expectedSuffix--) {
+			if (fExpected.charAt(expectedSuffix) != fActual.charAt(actualSuffix))
+				break;
+		}
+		fSuffix=  fExpected.length() - expectedSuffix;
+	}
+
+	private String computeCommonPrefix() {
+		return (fPrefix > fContextLength ? ELLIPSIS : "") + fExpected.substring(Math.max(0, fPrefix - fContextLength), fPrefix);
+	}
+
+	private String computeCommonSuffix() {
+		int end= Math.min(fExpected.length() - fSuffix + 1 + fContextLength, fExpected.length());
+		return fExpected.substring(fExpected.length() - fSuffix + 1, end) + (fExpected.length() - fSuffix + 1 < fExpected.length() - fContextLength ? ELLIPSIS : "");
+	}
+
+	private boolean areStringsEqual() {
+		return fExpected.equals(fActual);
+	}
+
+	// android-changed copy of Assert.format for reasons described above
+	private static String format(String message, Object expected, Object actual) {
+        	String formatted= "";
+        	if (message != null && message.length() > 0)
+            		formatted= message+" ";
+        	return formatted+"expected:<"+expected+"> but was:<"+actual+">";
+	}
+}
diff --git a/legacy-test/src/junit/framework/ComparisonFailure.java b/legacy-test/src/junit/framework/ComparisonFailure.java
new file mode 100644
index 0000000..5077993
--- /dev/null
+++ b/legacy-test/src/junit/framework/ComparisonFailure.java
@@ -0,0 +1,52 @@
+package junit.framework;
+
+/**
+ * Thrown when an assert equals for Strings failed.
+ * 
+ * Inspired by a patch from Alex Chaffee mailto:alex@purpletech.com
+ */
+public class ComparisonFailure extends AssertionFailedError {
+	private static final int MAX_CONTEXT_LENGTH= 20;
+	private static final long serialVersionUID= 1L;
+	
+	private String fExpected;
+	private String fActual;
+
+	/**
+	 * Constructs a comparison failure.
+	 * @param message the identifying message or null
+	 * @param expected the expected string value
+	 * @param actual the actual string value
+	 */
+	public ComparisonFailure (String message, String expected, String actual) {
+		super (message);
+		fExpected= expected;
+		fActual= actual;
+	}
+	
+	/**
+	 * Returns "..." in place of common prefix and "..." in
+	 * place of common suffix between expected and actual.
+	 * 
+	 * @see Throwable#getMessage()
+	 */
+	@Override
+	public String getMessage() {
+		return new ComparisonCompactor(MAX_CONTEXT_LENGTH, fExpected, fActual).compact(super.getMessage());
+	}
+	
+	/**
+	 * Gets the actual string value
+	 * @return the actual string value
+	 */
+	public String getActual() {
+		return fActual;
+	}
+	/**
+	 * Gets the expected string value
+	 * @return the expected string value
+	 */
+	public String getExpected() {
+		return fExpected;
+	}
+}
\ No newline at end of file
diff --git a/legacy-test/src/junit/framework/Protectable.java b/legacy-test/src/junit/framework/Protectable.java
new file mode 100644
index 0000000..e143237
--- /dev/null
+++ b/legacy-test/src/junit/framework/Protectable.java
@@ -0,0 +1,14 @@
+package junit.framework;
+
+/**
+ * A <em>Protectable</em> can be run and can throw a Throwable.
+ *
+ * @see TestResult
+ */
+public interface Protectable {
+
+	/**
+	 * Run the the following method protected.
+	 */
+	public abstract void protect() throws Throwable;
+}
\ No newline at end of file
diff --git a/legacy-test/src/junit/framework/Test.java b/legacy-test/src/junit/framework/Test.java
new file mode 100644
index 0000000..a016ee8
--- /dev/null
+++ b/legacy-test/src/junit/framework/Test.java
@@ -0,0 +1,17 @@
+package junit.framework;
+
+/**
+ * A <em>Test</em> can be run and collect its results.
+ *
+ * @see TestResult
+ */
+public interface Test {
+	/**
+	 * Counts the number of test cases that will be run by this test.
+	 */
+	public abstract int countTestCases();
+	/**
+	 * Runs a test and collects its result in a TestResult instance.
+	 */
+	public abstract void run(TestResult result);
+}
\ No newline at end of file
diff --git a/legacy-test/src/junit/framework/TestCase.java b/legacy-test/src/junit/framework/TestCase.java
new file mode 100644
index 0000000..b047ec9
--- /dev/null
+++ b/legacy-test/src/junit/framework/TestCase.java
@@ -0,0 +1,212 @@
+package junit.framework;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * A test case defines the fixture to run multiple tests. To define a test case<br/>
+ * <ol>
+ *   <li>implement a subclass of <code>TestCase</code></li>
+ *   <li>define instance variables that store the state of the fixture</li>
+ *   <li>initialize the fixture state by overriding {@link #setUp()}</li>
+ *   <li>clean-up after a test by overriding {@link #tearDown()}.</li>
+ * </ol>
+ * Each test runs in its own fixture so there
+ * can be no side effects among test runs.
+ * Here is an example:
+ * <pre>
+ * public class MathTest extends TestCase {
+ *    protected double fValue1;
+ *    protected double fValue2;
+ *
+ *    protected void setUp() {
+ *       fValue1= 2.0;
+ *       fValue2= 3.0;
+ *    }
+ * }
+ * </pre>
+ *
+ * For each test implement a method which interacts
+ * with the fixture. Verify the expected results with assertions specified
+ * by calling {@link junit.framework.Assert#assertTrue(String, boolean)} with a boolean.
+ * <pre>
+ *    public void testAdd() {
+ *       double result= fValue1 + fValue2;
+ *       assertTrue(result == 5.0);
+ *    }
+ * </pre>
+ * 
+ * Once the methods are defined you can run them. The framework supports
+ * both a static type safe and more dynamic way to run a test.
+ * In the static way you override the runTest method and define the method to
+ * be invoked. A convenient way to do so is with an anonymous inner class.
+ * <pre>
+ * TestCase test= new MathTest("add") {
+ *    public void runTest() {
+ *       testAdd();
+ *    }
+ * };
+ * test.run();
+ * </pre>
+ * 
+ * The dynamic way uses reflection to implement {@link #runTest()}. It dynamically finds
+ * and invokes a method.
+ * In this case the name of the test case has to correspond to the test method
+ * to be run.
+ * <pre>
+ * TestCase test= new MathTest("testAdd");
+ * test.run();
+ * </pre>
+ * 
+ * The tests to be run can be collected into a TestSuite. JUnit provides
+ * different <i>test runners</i> which can run a test suite and collect the results.
+ * A test runner either expects a static method <code>suite</code> as the entry
+ * point to get a test to run or it will extract the suite automatically.
+ * <pre>
+ * public static Test suite() {
+ *    suite.addTest(new MathTest("testAdd"));
+ *    suite.addTest(new MathTest("testDivideByZero"));
+ *    return suite;
+ * }
+ * </pre>
+ * @see TestResult
+ * @see TestSuite
+ */
+public abstract class TestCase extends Assert implements Test {
+	/**
+	 * the name of the test case
+	 */
+	private String fName;
+
+	/**
+	 * No-arg constructor to enable serialization. This method
+	 * is not intended to be used by mere mortals without calling setName().
+	 */
+	public TestCase() {
+		fName= null;
+	}
+	/**
+	 * Constructs a test case with the given name.
+	 */
+	public TestCase(String name) {
+		fName= name;
+	}
+	/**
+	 * Counts the number of test cases executed by run(TestResult result).
+	 */
+	public int countTestCases() {
+		return 1;
+	}
+	/**
+	 * Creates a default TestResult object
+	 *
+	 * @see TestResult
+	 */
+	protected TestResult createResult() {
+	    return new TestResult();
+	}
+	/**
+	 * A convenience method to run this test, collecting the results with a
+	 * default TestResult object.
+	 *
+	 * @see TestResult
+	 */
+	public TestResult run() {
+		TestResult result= createResult();
+		run(result);
+		return result;
+	}
+	/**
+	 * Runs the test case and collects the results in TestResult.
+	 */
+	public void run(TestResult result) {
+		result.run(this);
+	}
+	/**
+	 * Runs the bare test sequence.
+	 * @throws Throwable if any exception is thrown
+	 */
+	public void runBare() throws Throwable {
+		Throwable exception= null;
+		setUp();
+		try {
+			runTest();
+		} catch (Throwable running) {
+			exception= running;
+		}
+		finally {
+			try {
+				tearDown();
+			} catch (Throwable tearingDown) {
+				if (exception == null) exception= tearingDown;
+			}
+		}
+		if (exception != null) throw exception;
+	}
+	/**
+	 * Override to run the test and assert its state.
+	 * @throws Throwable if any exception is thrown
+	 */
+	protected void runTest() throws Throwable {
+		assertNotNull("TestCase.fName cannot be null", fName); // Some VMs crash when calling getMethod(null,null);
+		Method runMethod= null;
+		try {
+			// use getMethod to get all public inherited
+			// methods. getDeclaredMethods returns all
+			// methods of this class but excludes the
+			// inherited ones.
+			runMethod= getClass().getMethod(fName, (Class[])null);
+		} catch (NoSuchMethodException e) {
+			fail("Method \""+fName+"\" not found");
+		}
+		if (!Modifier.isPublic(runMethod.getModifiers())) {
+			fail("Method \""+fName+"\" should be public");
+		}
+
+		try {
+			runMethod.invoke(this);
+		}
+		catch (InvocationTargetException e) {
+			e.fillInStackTrace();
+			throw e.getTargetException();
+		}
+		catch (IllegalAccessException e) {
+			e.fillInStackTrace();
+			throw e;
+		}
+	}
+	/**
+	 * Sets up the fixture, for example, open a network connection.
+	 * This method is called before a test is executed.
+	 */
+	protected void setUp() throws Exception {
+	}
+	/**
+	 * Tears down the fixture, for example, close a network connection.
+	 * This method is called after a test is executed.
+	 */
+	protected void tearDown() throws Exception {
+	}
+	/**
+	 * Returns a string representation of the test case
+	 */
+	@Override
+	public String toString() {
+	    return getName() + "(" + getClass().getName() + ")";
+	}
+	/**
+	 * Gets the name of a TestCase
+	 * @return the name of the TestCase
+	 */
+	public String getName() {
+		return fName;
+	}
+	/**
+	 * Sets the name of a TestCase
+	 * @param name the name to set
+	 */
+	public void setName(String name) {
+		fName= name;
+	}
+}
diff --git a/legacy-test/src/junit/framework/TestFailure.java b/legacy-test/src/junit/framework/TestFailure.java
new file mode 100644
index 0000000..6662b1f
--- /dev/null
+++ b/legacy-test/src/junit/framework/TestFailure.java
@@ -0,0 +1,58 @@
+package junit.framework;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+
+/**
+ * A <code>TestFailure</code> collects a failed test together with
+ * the caught exception.
+ * @see TestResult
+ */
+public class TestFailure extends Object {
+	protected Test fFailedTest;
+	protected Throwable fThrownException;
+	
+
+	/**
+	 * Constructs a TestFailure with the given test and exception.
+	 */
+	public TestFailure(Test failedTest, Throwable thrownException) {
+		fFailedTest= failedTest;
+		fThrownException= thrownException;
+	}
+	/**
+	 * Gets the failed test.
+	 */
+	public Test failedTest() {
+	    return fFailedTest;
+	}
+	/**
+	 * Gets the thrown exception.
+	 */
+	public Throwable thrownException() {
+	    return fThrownException;
+	}
+	/**
+	 * Returns a short description of the failure.
+	 */
+	@Override
+	public String toString() {
+	    StringBuffer buffer= new StringBuffer();
+	    buffer.append(fFailedTest+": "+fThrownException.getMessage());
+	    return buffer.toString();
+	}
+	public String trace() {
+		StringWriter stringWriter= new StringWriter();
+		PrintWriter writer= new PrintWriter(stringWriter);
+		thrownException().printStackTrace(writer);
+		StringBuffer buffer= stringWriter.getBuffer();
+		return buffer.toString();
+	}
+	public String exceptionMessage() {
+		return thrownException().getMessage();
+	}
+	public boolean isFailure() {
+		return thrownException() instanceof AssertionFailedError;
+	}
+}
\ No newline at end of file
diff --git a/legacy-test/src/junit/framework/TestListener.java b/legacy-test/src/junit/framework/TestListener.java
new file mode 100644
index 0000000..9b69443
--- /dev/null
+++ b/legacy-test/src/junit/framework/TestListener.java
@@ -0,0 +1,23 @@
+package junit.framework;
+
+/**
+ * A Listener for test progress
+ */
+public interface TestListener {
+	/**
+ 	 * An error occurred.
+ 	 */
+	public void addError(Test test, Throwable t);
+	/**
+ 	 * A failure occurred.
+ 	 */
+ 	public void addFailure(Test test, AssertionFailedError t);  
+	/**
+	 * A test ended.
+	 */
+ 	public void endTest(Test test); 
+	/**
+	 * A test started.
+	 */
+	public void startTest(Test test);
+}
\ No newline at end of file
diff --git a/legacy-test/src/junit/framework/TestResult.java b/legacy-test/src/junit/framework/TestResult.java
new file mode 100644
index 0000000..3052e94
--- /dev/null
+++ b/legacy-test/src/junit/framework/TestResult.java
@@ -0,0 +1,174 @@
+package junit.framework;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * A <code>TestResult</code> collects the results of executing
+ * a test case. It is an instance of the Collecting Parameter pattern.
+ * The test framework distinguishes between <i>failures</i> and <i>errors</i>.
+ * A failure is anticipated and checked for with assertions. Errors are
+ * unanticipated problems like an {@link ArrayIndexOutOfBoundsException}.
+ *
+ * @see Test
+ */
+public class TestResult extends Object {
+	// BEGIN android-changed changed types from List<> to Vector<> for API compatibility
+	protected Vector<TestFailure> fFailures;
+	protected Vector<TestFailure> fErrors;
+	protected Vector<TestListener> fListeners;
+	// END android-changed
+	protected int fRunTests;
+	private boolean fStop;
+	
+	public TestResult() {
+		// BEGIN android-changed to Vector
+		fFailures= new Vector<TestFailure>();
+		fErrors= new Vector<TestFailure>();
+		fListeners= new Vector<TestListener>();
+		// END android-changed
+		fRunTests= 0;
+		fStop= false;
+	}
+	/**
+	 * Adds an error to the list of errors. The passed in exception
+	 * caused the error.
+	 */
+	public synchronized void addError(Test test, Throwable t) {
+		fErrors.add(new TestFailure(test, t));
+		for (TestListener each : cloneListeners())
+			each.addError(test, t);
+	}
+	/**
+	 * Adds a failure to the list of failures. The passed in exception
+	 * caused the failure.
+	 */
+	public synchronized void addFailure(Test test, AssertionFailedError t) {
+		fFailures.add(new TestFailure(test, t));
+		for (TestListener each : cloneListeners())
+			each.addFailure(test, t);
+	}
+	/**
+	 * Registers a TestListener
+	 */
+	public synchronized void addListener(TestListener listener) {
+		fListeners.add(listener);
+	}
+	/**
+	 * Unregisters a TestListener
+	 */
+	public synchronized void removeListener(TestListener listener) {
+		fListeners.remove(listener);
+	}
+	/**
+	 * Returns a copy of the listeners.
+	 */
+	private synchronized List<TestListener> cloneListeners() {
+		List<TestListener> result= new ArrayList<TestListener>();
+		result.addAll(fListeners);
+		return result;
+	}
+	/**
+	 * Informs the result that a test was completed.
+	 */
+	public void endTest(Test test) {
+		for (TestListener each : cloneListeners())
+			each.endTest(test);
+	}
+	/**
+	 * Gets the number of detected errors.
+	 */
+	public synchronized int errorCount() {
+		return fErrors.size();
+	}
+	/**
+	 * Returns an Enumeration for the errors
+	 */
+	public synchronized Enumeration<TestFailure> errors() {
+		return Collections.enumeration(fErrors);
+	}
+	
+
+	/**
+	 * Gets the number of detected failures.
+	 */
+	public synchronized int failureCount() {
+		return fFailures.size();
+	}
+	/**
+	 * Returns an Enumeration for the failures
+	 */
+	public synchronized Enumeration<TestFailure> failures() {
+		return Collections.enumeration(fFailures);
+	}
+	
+	/**
+	 * Runs a TestCase.
+	 */
+	protected void run(final TestCase test) {
+		startTest(test);
+		Protectable p= new Protectable() {
+			public void protect() throws Throwable {
+				test.runBare();
+			}
+		};
+		runProtected(test, p);
+
+		endTest(test);
+	}
+	/**
+	 * Gets the number of run tests.
+	 */
+	public synchronized int runCount() {
+		return fRunTests;
+	}
+	/**
+	 * Runs a TestCase.
+	 */
+	public void runProtected(final Test test, Protectable p) {
+		try {
+			p.protect();
+		} 
+		catch (AssertionFailedError e) {
+			addFailure(test, e);
+		}
+		catch (ThreadDeath e) { // don't catch ThreadDeath by accident
+			throw e;
+		}
+		catch (Throwable e) {
+			addError(test, e);
+		}
+	}
+	/**
+	 * Checks whether the test run should stop
+	 */
+	public synchronized boolean shouldStop() {
+		return fStop;
+	}
+	/**
+	 * Informs the result that a test will be started.
+	 */
+	public void startTest(Test test) {
+		final int count= test.countTestCases();
+		synchronized(this) {
+			fRunTests+= count;
+		}
+		for (TestListener each : cloneListeners())
+			each.startTest(test);
+	}
+	/**
+	 * Marks that the test run should stop.
+	 */
+	public synchronized void stop() {
+		fStop= true;
+	}
+	/**
+	 * Returns whether the entire test was successful or not.
+	 */
+	public synchronized boolean wasSuccessful() {
+		return failureCount() == 0 && errorCount() == 0;
+	}
+}
diff --git a/legacy-test/src/junit/framework/TestSuite.java b/legacy-test/src/junit/framework/TestSuite.java
new file mode 100644
index 0000000..336efd1
--- /dev/null
+++ b/legacy-test/src/junit/framework/TestSuite.java
@@ -0,0 +1,307 @@
+package junit.framework;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * <p>A <code>TestSuite</code> is a <code>Composite</code> of Tests.
+ * It runs a collection of test cases. Here is an example using
+ * the dynamic test definition.
+ * <pre>
+ * TestSuite suite= new TestSuite();
+ * suite.addTest(new MathTest("testAdd"));
+ * suite.addTest(new MathTest("testDivideByZero"));
+ * </pre>
+ * </p>
+ * 
+ * <p>Alternatively, a TestSuite can extract the tests to be run automatically.
+ * To do so you pass the class of your TestCase class to the
+ * TestSuite constructor.
+ * <pre>
+ * TestSuite suite= new TestSuite(MathTest.class);
+ * </pre>
+ * </p>
+ * 
+ * <p>This constructor creates a suite with all the methods
+ * starting with "test" that take no arguments.</p>
+ * 
+ * <p>A final option is to do the same for a large array of test classes.
+ * <pre>
+ * Class[] testClasses = { MathTest.class, AnotherTest.class }
+ * TestSuite suite= new TestSuite(testClasses);
+ * </pre>
+ * </p>
+ *
+ * @see Test
+ */
+public class TestSuite implements Test {
+
+	/**
+	 * ...as the moon sets over the early morning Merlin, Oregon
+	 * mountains, our intrepid adventurers type...
+	 */
+	static public Test createTest(Class<?> theClass, String name) {
+		Constructor<?> constructor;
+		try {
+			constructor= getTestConstructor(theClass);
+		} catch (NoSuchMethodException e) {
+			return warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()");
+		}
+		Object test;
+		try {
+			if (constructor.getParameterTypes().length == 0) {
+				test= constructor.newInstance(new Object[0]);
+				if (test instanceof TestCase)
+					((TestCase) test).setName(name);
+			} else {
+				test= constructor.newInstance(new Object[]{name});
+			}
+		} catch (InstantiationException e) {
+			return(warning("Cannot instantiate test case: "+name+" ("+exceptionToString(e)+")"));
+		} catch (InvocationTargetException e) {
+			return(warning("Exception in constructor: "+name+" ("+exceptionToString(e.getTargetException())+")"));
+		} catch (IllegalAccessException e) {
+			return(warning("Cannot access test case: "+name+" ("+exceptionToString(e)+")"));
+		}
+		return (Test) test;
+	}
+	
+	/**
+	 * Gets a constructor which takes a single String as
+	 * its argument or a no arg constructor.
+	 */
+	public static Constructor<?> getTestConstructor(Class<?> theClass) throws NoSuchMethodException {
+		try {
+			return theClass.getConstructor(String.class);	
+		} catch (NoSuchMethodException e) {
+			// fall through
+		}
+		return theClass.getConstructor(new Class[0]);
+	}
+
+	/**
+	 * Returns a test which will fail and log a warning message.
+	 */
+	public static Test warning(final String message) {
+		return new TestCase("warning") {
+			@Override
+			protected void runTest() {
+				fail(message);
+			}
+		};
+	}
+
+	/**
+	 * Converts the stack trace into a string
+	 */
+	private static String exceptionToString(Throwable t) {
+		StringWriter stringWriter= new StringWriter();
+		PrintWriter writer= new PrintWriter(stringWriter);
+		t.printStackTrace(writer);
+		return stringWriter.toString();
+	}
+	
+	private String fName;
+
+	private Vector<Test> fTests= new Vector<Test>(10); // Cannot convert this to List because it is used directly by some test runners
+
+    /**
+	 * Constructs an empty TestSuite.
+	 */
+	public TestSuite() {
+	}
+	
+	/**
+	 * Constructs a TestSuite from the given class. Adds all the methods
+	 * starting with "test" as test cases to the suite.
+	 * Parts of this method were written at 2337 meters in the Hueffihuette,
+	 * Kanton Uri
+	 */
+	public TestSuite(final Class<?> theClass) {
+		addTestsFromTestCase(theClass);
+	}
+
+	private void addTestsFromTestCase(final Class<?> theClass) {
+		fName= theClass.getName();
+		try {
+			getTestConstructor(theClass); // Avoid generating multiple error messages
+		} catch (NoSuchMethodException e) {
+			addTest(warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()"));
+			return;
+		}
+
+		if (!Modifier.isPublic(theClass.getModifiers())) {
+			addTest(warning("Class "+theClass.getName()+" is not public"));
+			return;
+		}
+
+		Class<?> superClass= theClass;
+		List<String> names= new ArrayList<String>();
+		while (Test.class.isAssignableFrom(superClass)) {
+			for (Method each : superClass.getDeclaredMethods())
+				addTestMethod(each, names, theClass);
+			superClass= superClass.getSuperclass();
+		}
+		if (fTests.size() == 0)
+			addTest(warning("No tests found in "+theClass.getName()));
+	}
+	
+	/**
+	 * Constructs a TestSuite from the given class with the given name.
+	 * @see TestSuite#TestSuite(Class)
+	 */
+	public TestSuite(Class<? extends TestCase>  theClass, String name) {
+		this(theClass);
+		setName(name);
+	}
+	
+   	/**
+	 * Constructs an empty TestSuite.
+	 */
+	public TestSuite(String name) {
+		setName(name);
+	}
+	
+	/**
+	 * Constructs a TestSuite from the given array of classes.  
+	 * @param classes {@link TestCase}s
+	 */
+	public TestSuite (Class<?>... classes) {
+		for (Class<?> each : classes)
+			addTest(testCaseForClass(each));
+	}
+
+	private Test testCaseForClass(Class<?> each) {
+		if (TestCase.class.isAssignableFrom(each))
+			return new TestSuite(each.asSubclass(TestCase.class));
+		else
+			return warning(each.getCanonicalName() + " does not extend TestCase");
+	}
+	
+	/**
+	 * Constructs a TestSuite from the given array of classes with the given name.
+	 * @see TestSuite#TestSuite(Class[])
+	 */
+	public TestSuite(Class<? extends TestCase>[] classes, String name) {
+		this(classes);
+		setName(name);
+	}
+	
+	/**
+	 * Adds a test to the suite.
+	 */
+	public void addTest(Test test) {
+		fTests.add(test);
+	}
+
+	/**
+	 * Adds the tests from the given class to the suite
+	 */
+	public void addTestSuite(Class<? extends TestCase> testClass) {
+		addTest(new TestSuite(testClass));
+	}
+	
+	/**
+	 * Counts the number of test cases that will be run by this test.
+	 */
+	public int countTestCases() {
+		int count= 0;
+		for (Test each : fTests)
+			count+=  each.countTestCases();
+		return count;
+	}
+
+	/**
+	 * Returns the name of the suite. Not all
+	 * test suites have a name and this method
+	 * can return null.
+	 */
+	public String getName() {
+		return fName;
+	}
+	 
+	/**
+	 * Runs the tests and collects their result in a TestResult.
+	 */
+	public void run(TestResult result) {
+		for (Test each : fTests) {
+	  		if (result.shouldStop() )
+	  			break;
+			runTest(each, result);
+		}
+	}
+
+	public void runTest(Test test, TestResult result) {
+		test.run(result);
+	}
+	 
+	/**
+	 * Sets the name of the suite.
+	 * @param name the name to set
+	 */
+	public void setName(String name) {
+		fName= name;
+	}
+
+	/**
+	 * Returns the test at the given index
+	 */
+	public Test testAt(int index) {
+		return fTests.get(index);
+	}
+	
+	/**
+	 * Returns the number of tests in this suite
+	 */
+	public int testCount() {
+		return fTests.size();
+	}
+	
+	/**
+	 * Returns the tests as an enumeration
+	 */
+	public Enumeration<Test> tests() {
+		return fTests.elements();
+	}
+	
+	/**
+	 */
+	@Override
+	public String toString() {
+		if (getName() != null)
+			return getName();
+		return super.toString();
+	 }
+
+	private void addTestMethod(Method m, List<String> names, Class<?> theClass) {
+		String name= m.getName();
+		if (names.contains(name))
+			return;
+		if (! isPublicTestMethod(m)) {
+			if (isTestMethod(m))
+				addTest(warning("Test method isn't public: "+ m.getName() + "(" + theClass.getCanonicalName() + ")"));
+			return;
+		}
+		names.add(name);
+		addTest(createTest(theClass, name));
+	}
+
+	private boolean isPublicTestMethod(Method m) {
+		return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
+	 }
+	 
+	private boolean isTestMethod(Method m) {
+		return 
+			m.getParameterTypes().length == 0 && 
+			m.getName().startsWith("test") && 
+			m.getReturnType().equals(Void.TYPE);
+	 }
+}
\ No newline at end of file
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index 489039c..dbbf00d 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -225,7 +225,7 @@
     bool outlineIsRounded = MathUtils::isPositive(radius);
     if (!outlineIsRounded || currentTransform()->isSimple()) {
         // TODO: consider storing this rect separately, so that this can't be replaced with clip ops
-        clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, kIntersect_SkClipOp);
+        clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, SkClipOp::kIntersect);
     }
     if (outlineIsRounded) {
         setClippingRoundRect(allocator, bounds, radius, false);
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 5b683e0..a53a55a 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -120,7 +120,7 @@
     mCanvasState.save(SaveFlags::MatrixClip);
     mCanvasState.translate(tx, ty);
     mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
-            kIntersect_SkClipOp);
+            SkClipOp::kIntersect);
     deferNodePropsAndOps(renderNode);
     mCanvasState.restore();
 }
@@ -262,7 +262,7 @@
         Rect clipRect;
         properties.getClippingRectForFlags(clipFlags, &clipRect);
         mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
-                kIntersect_SkClipOp);
+                SkClipOp::kIntersect);
     }
 
     if (properties.getRevealClip().willClip()) {
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
index 52c62cc..983c17e 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -36,8 +36,7 @@
         , mSize(0)
         , mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity)
         , mMeshBuffer(0)
-        , mFreeBlocks(nullptr)
-        , mGenerationId(0) {}
+        , mFreeBlocks(nullptr) {}
 
 PatchCache::~PatchCache() {
     clear();
@@ -158,7 +157,6 @@
         mMaxSize, nullptr, GL_DYNAMIC_DRAW);
     mSize = 0;
     mFreeBlocks = new BufferBlock(0, mMaxSize);
-    mGenerationId++;
 }
 
 /**
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
index 0624c35..aa746c7 100644
--- a/libs/hwui/PatchCache.h
+++ b/libs/hwui/PatchCache.h
@@ -69,10 +69,6 @@
         return mMeshBuffer;
     }
 
-    uint32_t getGenerationId() const {
-        return mGenerationId;
-    }
-
     /**
      * Removes the entries associated with the specified 9-patch. This is meant
      * to be called from threads that are not the EGL context thread (GC thread
@@ -175,8 +171,6 @@
     // First available free block inside the mesh buffer
     BufferBlock* mFreeBlocks;
 
-    uint32_t mGenerationId;
-
     // Garbage tracking, required to handle GC events on the VM side
     Vector<Res_png_9patch*> mGarbage;
     mutable Mutex mLock;
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 287e58a..4f92657 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -115,7 +115,7 @@
      * the specified operation. The specified rectangle is considered
      * already transformed.
      */
-    void clipTransformed(const Rect& r, SkClipOp op = kIntersect_SkClipOp);
+    void clipTransformed(const Rect& r, SkClipOp op = SkClipOp::kIntersect);
 
     /**
      * Modifies the current clip with the specified region and operation.
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 64ef866..2177af1 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -106,6 +106,8 @@
         return PIXEL_FORMAT_RGBA_8888;
     case GL_RGBA:
         return PIXEL_FORMAT_RGBA_8888;
+    case GL_RGB:
+        return PIXEL_FORMAT_RGB_565;
     default:
         LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", internalFormat);
         return PIXEL_FORMAT_UNKNOWN;
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 14cc449..117395b 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -68,7 +68,7 @@
         if (pendingClip && !pendingClip->contains(rect)) {
             canvas->clipRect(*pendingClip);
         }
-        canvas->clipRRect(SkRRect::MakeRectXY(rect, radius, radius), kIntersect_SkClipOp, true);
+        canvas->clipRRect(SkRRect::MakeRectXY(rect, radius, radius), SkClipOp::kIntersect, true);
     } else {
         if (pendingClip) {
             (void)rect.intersect(*pendingClip);
@@ -263,7 +263,7 @@
     }
 
     if (properties.getRevealClip().willClip()) {
-        canvas->clipPath(*properties.getRevealClip().getPath(), kIntersect_SkClipOp, true);
+        canvas->clipPath(*properties.getRevealClip().getPath(), SkClipOp::kIntersect, true);
     } else if (properties.getOutline().willClip()) {
         clipOutline(properties.getOutline(), canvas, pendingClip);
         pendingClip = nullptr;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 5ba0f42..376346f 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -100,7 +100,7 @@
             int saveCount = layerCanvas->save();
             SkASSERT(saveCount == 1);
 
-            layerCanvas->clipRect(layerDamage.toSkRect(), kReplace_SkClipOp);
+            layerCanvas->clipRect(layerDamage.toSkRect(), SkClipOp::kReplace);
 
             auto savedLightCenter = mLightCenter;
             // map current light center into RenderNode's coordinate space
@@ -234,7 +234,7 @@
         const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds,
         SkCanvas* canvas) {
 
-    canvas->clipRect(clip, kReplace_SkClipOp);
+    canvas->clipRect(clip, SkClipOp::kReplace);
 
     if (!opaque) {
         canvas->clear(SK_ColorTRANSPARENT);
diff --git a/include/private/README b/libs/hwui/private/README
similarity index 100%
rename from include/private/README
rename to libs/hwui/private/README
diff --git a/include/private/hwui/DrawGlInfo.h b/libs/hwui/private/hwui/DrawGlInfo.h
similarity index 100%
rename from include/private/hwui/DrawGlInfo.h
rename to libs/hwui/private/hwui/DrawGlInfo.h
diff --git a/libs/hwui/tests/common/BitmapAllocationTestUtils.h b/libs/hwui/tests/common/BitmapAllocationTestUtils.h
index 6dadd3e3..4892179 100644
--- a/libs/hwui/tests/common/BitmapAllocationTestUtils.h
+++ b/libs/hwui/tests/common/BitmapAllocationTestUtils.h
@@ -39,7 +39,9 @@
     static sk_sp<Bitmap> allocateHardwareBitmap(int width, int height,
             SkColorType colorType, std::function<void(SkBitmap& bitmap)> setup) {
         SkBitmap skBitmap;
-        sk_sp<Bitmap> heapBitmap(TestUtils::createBitmap(width, height, &skBitmap));
+        SkImageInfo info = SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType);
+        skBitmap.setInfo(info);
+        sk_sp<Bitmap> heapBitmap(Bitmap::allocateHeapBitmap(&skBitmap, nullptr));
         setup(skBitmap);
         return Bitmap::allocateHardwareBitmap(skBitmap);
     }
diff --git a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
index 45443b0..f47e05a 100644
--- a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
@@ -34,11 +34,11 @@
                 [](RenderProperties& props, Canvas& canvas) {
             canvas.save(SaveFlags::MatrixClip);
             {
-                canvas.clipRect(0, 0, 200, 200, kIntersect_SkClipOp);
+                canvas.clipRect(0, 0, 200, 200, SkClipOp::kIntersect);
                 canvas.translate(100, 100);
                 canvas.rotate(45);
                 canvas.translate(-100, -100);
-                canvas.clipRect(0, 0, 200, 200, kIntersect_SkClipOp);
+                canvas.clipRect(0, 0, 200, 200, SkClipOp::kIntersect);
                 canvas.drawColor(Color::Blue_500, SkBlendMode::kSrcOver);
             }
             canvas.restore();
@@ -47,7 +47,7 @@
             {
                 SkPath clipCircle;
                 clipCircle.addCircle(100, 300, 100);
-                canvas.clipPath(&clipCircle, kIntersect_SkClipOp);
+                canvas.clipPath(&clipCircle, SkClipOp::kIntersect);
                 canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
             }
             canvas.restore();
diff --git a/libs/hwui/tests/common/scenes/HwBitmap565.cpp b/libs/hwui/tests/common/scenes/HwBitmap565.cpp
new file mode 100644
index 0000000..18fea3d
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/HwBitmap565.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestSceneBase.h"
+#include "utils/Color.h"
+#include "tests/common/BitmapAllocationTestUtils.h"
+
+class HwBitmap565;
+
+static TestScene::Registrar _HwBitmap565(TestScene::Info{
+    "hwBitmap565",
+    "Draws composite shader with hardware bitmap",
+    TestScene::simpleCreateScene<HwBitmap565>
+});
+
+class HwBitmap565 : public TestScene {
+public:
+    sp<RenderNode> card;
+    void createContent(int width, int height, Canvas& canvas) override {
+        canvas.drawColor(Color::Grey_200, SkBlendMode::kSrcOver);
+
+        sk_sp<Bitmap> hardwareBitmap = BitmapAllocationTestUtils::allocateHardwareBitmap(200, 200,
+                kRGB_565_SkColorType, [](SkBitmap& skBitmap) {
+            skBitmap.eraseColor(Color::White);
+            SkCanvas skCanvas(skBitmap);
+            SkPaint skPaint;
+            skPaint.setColor(Color::Red_500);
+            skCanvas.drawRect(SkRect::MakeWH(100, 100), skPaint);
+            skPaint.setColor(Color::Blue_500);
+            skCanvas.drawRect(SkRect::MakeXYWH(100, 100, 100, 100), skPaint);
+        });
+        canvas.drawBitmap(*hardwareBitmap, 10.0f, 10.0f, nullptr);
+    }
+
+    void doFrame(int frameNr) override { }
+};
\ No newline at end of file
diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
index 8b2852b..7e8a7d9 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -36,7 +36,7 @@
             // nested clipped saveLayers
             canvas.saveLayerAlpha(0, 0, 400, 400, 200, SaveFlags::ClipToLayer);
             canvas.drawColor(Color::Green_700, SkBlendMode::kSrcOver);
-            canvas.clipRect(50, 50, 350, 350, kIntersect_SkClipOp);
+            canvas.clipRect(50, 50, 350, 350, SkClipOp::kIntersect);
             canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
             canvas.drawColor(Color::Blue_500, SkBlendMode::kSrcOver);
             canvas.restore();
diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
index d44c3d5..09e70eb 100644
--- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
@@ -82,7 +82,7 @@
                     int middleCount = canvas.save(SaveFlags::MatrixClip);
                     for (auto op : ops) {
                         int innerCount = canvas.save(SaveFlags::MatrixClip);
-                        canvas.clipRect(0, 0, cellSize, cellSize, kIntersect_SkClipOp);
+                        canvas.clipRect(0, 0, cellSize, cellSize, SkClipOp::kIntersect);
                         canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
                         op(canvas, cellSize, paint);
                         canvas.restoreToCount(innerCount);
diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
index 3ef0d1c..f166c5c 100644
--- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
@@ -177,7 +177,7 @@
         // Clip to padding
         // Can expect ~25% of views to have clip to padding with a non-null padding
         int clipRestoreCount = canvas->save(SaveFlags::MatrixClip);
-        canvas->clipRect(1, 1, 199, 199, kIntersect_SkClipOp);
+        canvas->clipRect(1, 1, 199, 199, SkClipOp::kIntersect);
 
         canvas->insertReorderBarrier(true);
 
diff --git a/libs/hwui/tests/unit/CanvasStateTests.cpp b/libs/hwui/tests/unit/CanvasStateTests.cpp
index 7555fd4..43974f6 100644
--- a/libs/hwui/tests/unit/CanvasStateTests.cpp
+++ b/libs/hwui/tests/unit/CanvasStateTests.cpp
@@ -68,13 +68,13 @@
     state.initializeSaveStack(200, 200,
             0, 0, 200, 200, Vector3());
 
-    state.clipRect(0, 0, 100, 100, kIntersect_SkClipOp);
+    state.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
     ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(100, 100));
 
-    state.clipRect(10, 10, 200, 200, kIntersect_SkClipOp);
+    state.clipRect(10, 10, 200, 200, SkClipOp::kIntersect);
     ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10, 100, 100));
 
-    state.clipRect(50, 50, 150, 150, kReplace_SkClipOp);
+    state.clipRect(50, 50, 150, 150, SkClipOp::kReplace);
     ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(50, 50, 150, 150));
 }
 
@@ -88,7 +88,7 @@
         // rotated clip causes complex clip
         state.rotate(10);
         EXPECT_TRUE(state.clipIsSimple());
-        state.clipRect(0, 0, 200, 200, kIntersect_SkClipOp);
+        state.clipRect(0, 0, 200, 200, SkClipOp::kIntersect);
         EXPECT_FALSE(state.clipIsSimple());
     }
     state.restore();
@@ -97,7 +97,7 @@
     {
         // subtracted clip causes complex clip
         EXPECT_TRUE(state.clipIsSimple());
-        state.clipRect(50, 50, 150, 150, kDifference_SkClipOp);
+        state.clipRect(50, 50, 150, 150, SkClipOp::kDifference);
         EXPECT_FALSE(state.clipIsSimple());
     }
     state.restore();
@@ -108,7 +108,7 @@
         SkPath path;
         path.addOval(SkRect::MakeWH(200, 200));
         EXPECT_TRUE(state.clipIsSimple());
-        state.clipPath(&path, kDifference_SkClipOp);
+        state.clipPath(&path, SkClipOp::kDifference);
         EXPECT_FALSE(state.clipIsSimple());
     }
     state.restore();
@@ -121,7 +121,7 @@
 
     state.save(SaveFlags::Clip);
     {
-        state.clipRect(0, 0, 10, 10, kIntersect_SkClipOp);
+        state.clipRect(0, 0, 10, 10, SkClipOp::kIntersect);
         ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
     }
     state.restore();
@@ -145,7 +145,7 @@
 
     state.save(SaveFlags::Matrix); // NOTE: clip not saved
     {
-        state.clipRect(0, 0, 10, 10, kIntersect_SkClipOp);
+        state.clipRect(0, 0, 10, 10, SkClipOp::kIntersect);
         ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
     }
     state.restore();
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index 12622ff..21394ae 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -174,7 +174,7 @@
     auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(200, 200, 400, 400, kIntersect_SkClipOp); // intersection should be empty
+        canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect); // intersection should be empty
         canvas.drawRect(0, 0, 400, 400, SkPaint());
         canvas.restore();
     });
@@ -453,19 +453,19 @@
         sk_sp<Bitmap> bitmap(TestUtils::createBitmap(20, 20));
 
         // left side clipped (to inset left half)
-        canvas.clipRect(10, 0, 50, 100, kReplace_SkClipOp);
+        canvas.clipRect(10, 0, 50, 100, SkClipOp::kReplace);
         canvas.drawBitmap(*bitmap, 0, 40, nullptr);
 
         // top side clipped (to inset top half)
-        canvas.clipRect(0, 10, 100, 50, kReplace_SkClipOp);
+        canvas.clipRect(0, 10, 100, 50, SkClipOp::kReplace);
         canvas.drawBitmap(*bitmap, 40, 0, nullptr);
 
         // right side clipped (to inset right half)
-        canvas.clipRect(50, 0, 90, 100, kReplace_SkClipOp);
+        canvas.clipRect(50, 0, 90, 100, SkClipOp::kReplace);
         canvas.drawBitmap(*bitmap, 80, 40, nullptr);
 
         // bottom not clipped, just abutting (inset bottom half)
-        canvas.clipRect(0, 50, 100, 90, kReplace_SkClipOp);
+        canvas.clipRect(0, 50, 100, 90, SkClipOp::kReplace);
         canvas.drawBitmap(*bitmap, 40, 70, nullptr);
     });
 
@@ -488,7 +488,7 @@
         SkPath path;
         path.addCircle(200, 200, 200, SkPath::kCW_Direction);
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipPath(&path, kIntersect_SkClipOp);
+        canvas.clipPath(&path, SkClipOp::kIntersect);
         SkPaint paint;
         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
         paint.setAntiAlias(true);
@@ -649,7 +649,7 @@
     auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
             [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(50, 50, 150, 150, kIntersect_SkClipOp);
+        canvas.clipRect(50, 50, 150, 150, SkClipOp::kIntersect);
         canvas.drawLayer(layerUpdater.get());
         canvas.restore();
     });
@@ -973,7 +973,7 @@
         auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
                 [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(200, 200, 400, 400, kIntersect_SkClipOp);
+        canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect);
         canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer);
 
         // draw within save layer may still be recorded, but shouldn't be drawn
@@ -1781,7 +1781,7 @@
     auto child = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
             [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
         // Record time clip will be ignored by projectee
-        canvas.clipRect(100, 100, 300, 300, kIntersect_SkClipOp);
+        canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect);
 
         canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
         canvas.drawRenderNode(projectingRipple.get());
@@ -1993,7 +1993,7 @@
             [](RenderProperties& props, RecordingCanvas& canvas) {
         // Apply a clip before the reorder barrier/shadow casting child is drawn.
         // This clip must be applied to the shadow cast by the child.
-        canvas.clipRect(25, 25, 75, 75, kIntersect_SkClipOp);
+        canvas.clipRect(25, 25, 75, 75, SkClipOp::kIntersect);
         canvas.insertReorderBarrier(true);
         canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
     });
@@ -2252,7 +2252,7 @@
     };
     auto node = TestUtils::createNode<RecordingCanvas>(20, 20, 30, 30,
             [](RenderProperties& props, RecordingCanvas& canvas) {
-        canvas.clipRect(0, -20, 10, 30, kReplace_SkClipOp);
+        canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace);
         canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
     });
 
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 70c9391..4a73383 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -58,7 +58,7 @@
 TEST(RecordingCanvas, clipRect) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(0, 0, 100, 100, kIntersect_SkClipOp);
+        canvas.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
         canvas.drawRect(0, 0, 50, 50, SkPaint());
         canvas.drawRect(50, 50, 100, 100, SkPaint());
         canvas.restore();
@@ -74,8 +74,8 @@
 TEST(RecordingCanvas, emptyClipRect) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(0, 0, 100, 100, kIntersect_SkClipOp);
-        canvas.clipRect(100, 100, 200, 200, kIntersect_SkClipOp);
+        canvas.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
+        canvas.clipRect(100, 100, 200, 200, SkClipOp::kIntersect);
         canvas.drawRect(0, 0, 50, 50, SkPaint()); // rejected at record time
         canvas.restore();
     });
@@ -441,7 +441,7 @@
 TEST(RecordingCanvas, saveLayer_addClipFlag) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(10, 20, 190, 180, kIntersect_SkClipOp);
+        canvas.clipRect(10, 20, 190, 180, SkClipOp::kIntersect);
         canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
         canvas.drawRect(10, 20, 190, 180, SkPaint());
         canvas.restore();
@@ -460,7 +460,7 @@
 TEST(RecordingCanvas, saveLayer_viewportCrop) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         // shouldn't matter, since saveLayer will clip to its bounds
-        canvas.clipRect(-1000, -1000, 1000, 1000, kReplace_SkClipOp);
+        canvas.clipRect(-1000, -1000, 1000, 1000, SkClipOp::kReplace);
 
         canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
         canvas.drawRect(0, 0, 400, 400, SkPaint());
@@ -550,7 +550,7 @@
         canvas.save(SaveFlags::MatrixClip);
         canvas.translate(0, -20); // avoid identity case
         // empty clip rect should force layer + contents to be rejected
-        canvas.clipRect(0, -20, 200, -20, kIntersect_SkClipOp);
+        canvas.clipRect(0, -20, 200, -20, SkClipOp::kIntersect);
         canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
         canvas.drawRect(0, 0, 200, 200, SkPaint());
         canvas.restore();
@@ -569,7 +569,7 @@
     });
 
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&child](RecordingCanvas& canvas) {
-        canvas.clipRect(0, 0, 0, 0, kIntersect_SkClipOp); // empty clip, reject node
+        canvas.clipRect(0, 0, 0, 0, SkClipOp::kIntersect); // empty clip, reject node
         canvas.drawRenderNode(child.get()); // shouldn't crash when rejecting node...
     });
     ASSERT_TRUE(dl->isEmpty());
@@ -622,7 +622,7 @@
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         // since no explicit clip set on canvas, this should be the one observed on op:
-        canvas.clipRect(-100, -100, 300, 300, kIntersect_SkClipOp);
+        canvas.clipRect(-100, -100, 300, 300, SkClipOp::kIntersect);
 
         SkPaint paint;
         paint.setColor(SK_ColorWHITE);
@@ -638,7 +638,7 @@
 TEST(RecordingCanvas, replaceClipIntersectWithRoot) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(-10, -10, 110, 110, kReplace_SkClipOp);
+        canvas.clipRect(-10, -10, 110, 110, SkClipOp::kReplace);
         canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
         canvas.restore();
     });
@@ -676,7 +676,7 @@
         canvas.drawRect(0, 0, 400, 400, SkPaint());
 
         // second chunk: no recorded clip, since inorder region
-        canvas.clipRect(0, 0, 200, 200, kIntersect_SkClipOp);
+        canvas.clipRect(0, 0, 200, 200, SkClipOp::kIntersect);
         canvas.insertReorderBarrier(false);
         canvas.drawRect(0, 0, 400, 400, SkPaint());
 
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 4d4705c..65eb169 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -455,7 +455,7 @@
     auto child = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
             [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
         // Record time clip will be ignored by projectee
-        canvas.clipRect(100, 100, 300, 300, kIntersect_SkClipOp);
+        canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect);
 
         canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
         canvas.drawRenderNode(projectingRipple.get());
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 06f8693..9e20498 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -339,7 +339,7 @@
     std::vector<sp<RenderNode>> nodes;
     nodes.push_back(TestUtils::createSkiaNode(20, 20, 30, 30,
             [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
-        canvas.clipRect(0, -20, 10, 30, kReplace_SkClipOp);
+        canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace);
         canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
     }));
 
diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp
index 598cd1e..8b80d69 100644
--- a/libs/hwui/utils/TestWindowContext.cpp
+++ b/libs/hwui/utils/TestWindowContext.cpp
@@ -93,7 +93,7 @@
 
     SkCanvas* prepareToDraw() {
         //mCanvas->reset(mSize.width(), mSize.height());
-        mCanvas->clipRect(0, 0, mSize.width(), mSize.height(), kReplace_SkClipOp);
+        mCanvas->clipRect(0, 0, mSize.width(), mSize.height(), SkClipOp::kReplace);
         return mCanvas->asSkCanvas();
     }
 
diff --git a/libs/storage/Android.bp b/libs/storage/Android.bp
new file mode 100644
index 0000000..911bd1d
--- /dev/null
+++ b/libs/storage/Android.bp
@@ -0,0 +1,19 @@
+cc_library_static {
+    name: "libstorage",
+
+    srcs: [
+        "IMountServiceListener.cpp",
+        "IMountShutdownObserver.cpp",
+        "IObbActionListener.cpp",
+        "IMountService.cpp",
+    ],
+
+    export_include_dirs: ["include"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: ["libbinder"],
+}
diff --git a/libs/storage/Android.mk b/libs/storage/Android.mk
deleted file mode 100644
index d0eb6d4..0000000
--- a/libs/storage/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	IMountServiceListener.cpp \
-	IMountShutdownObserver.cpp \
-	IObbActionListener.cpp \
-	IMountService.cpp
-
-LOCAL_MODULE:= libstorage
-
-LOCAL_CFLAGS += -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := libbinder
-
-include $(BUILD_STATIC_LIBRARY)
diff --git a/include/storage/IMountService.h b/libs/storage/include/storage/IMountService.h
similarity index 100%
rename from include/storage/IMountService.h
rename to libs/storage/include/storage/IMountService.h
diff --git a/include/storage/IMountServiceListener.h b/libs/storage/include/storage/IMountServiceListener.h
similarity index 100%
rename from include/storage/IMountServiceListener.h
rename to libs/storage/include/storage/IMountServiceListener.h
diff --git a/include/storage/IMountShutdownObserver.h b/libs/storage/include/storage/IMountShutdownObserver.h
similarity index 100%
rename from include/storage/IMountShutdownObserver.h
rename to libs/storage/include/storage/IMountShutdownObserver.h
diff --git a/include/storage/IObbActionListener.h b/libs/storage/include/storage/IObbActionListener.h
similarity index 100%
rename from include/storage/IObbActionListener.h
rename to libs/storage/include/storage/IObbActionListener.h
diff --git a/media/java/android/media/AmrInputStream.java b/media/java/android/media/AmrInputStream.java
new file mode 100644
index 0000000..fb91bbb
--- /dev/null
+++ b/media/java/android/media/AmrInputStream.java
@@ -0,0 +1,195 @@
+/*
+ * 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 android.media;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import android.media.MediaCodec.BufferInfo;
+import android.util.Log;
+
+
+/**
+ * AmrInputStream
+ * @hide
+ */
+public final class AmrInputStream extends InputStream {
+    private final static String TAG = "AmrInputStream";
+    
+    // frame is 20 msec at 8.000 khz
+    private final static int SAMPLES_PER_FRAME = 8000 * 20 / 1000;
+
+    MediaCodec mCodec;
+    BufferInfo mInfo;
+    boolean mSawOutputEOS;
+    boolean mSawInputEOS;
+
+    // pcm input stream
+    private InputStream mInputStream;
+
+    // result amr stream
+    private final byte[] mBuf = new byte[SAMPLES_PER_FRAME * 2];
+    private int mBufIn = 0;
+    private int mBufOut = 0;
+
+    // helper for bytewise read()
+    private byte[] mOneByte = new byte[1];
+
+    /**
+     * Create a new AmrInputStream, which converts 16 bit PCM to AMR
+     * @param inputStream InputStream containing 16 bit PCM.
+     */
+    public AmrInputStream(InputStream inputStream) {
+        mInputStream = inputStream;
+
+        MediaFormat format  = new MediaFormat();
+        format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AMR_NB);
+        format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 8000);
+        format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
+        format.setInteger(MediaFormat.KEY_BIT_RATE, 12200);
+
+        MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+        String name = mcl.findEncoderForFormat(format);
+        if (name != null) {
+            try {
+                mCodec = MediaCodec.createByCodecName(name);
+                mCodec.configure(format,
+                        null /* surface */,
+                        null /* crypto */,
+                        MediaCodec.CONFIGURE_FLAG_ENCODE);
+                mCodec.start();
+            } catch (IOException e) {
+                if (mCodec != null) {
+                    mCodec.release();
+                }
+                mCodec = null;
+            }
+        }
+        mInfo = new BufferInfo();
+    }
+
+    @Override
+    public int read() throws IOException {
+        int rtn = read(mOneByte, 0, 1);
+        return rtn == 1 ? (0xff & mOneByte[0]) : -1;
+    }
+
+    @Override
+    public int read(byte[] b) throws IOException {
+        return read(b, 0, b.length);
+    }
+
+    @Override
+    public int read(byte[] b, int offset, int length) throws IOException {
+        if (mCodec == null) {
+            throw new IllegalStateException("not open");
+        }
+
+        if (mBufOut >= mBufIn && !mSawOutputEOS) {
+            // no data left in buffer, refill it
+            mBufOut = 0;
+            mBufIn = 0;
+
+            // first push as much data into the encoder as possible
+            while (!mSawInputEOS) {
+                int index = mCodec.dequeueInputBuffer(0);
+                if (index < 0) {
+                    // no input buffer currently available
+                    break;
+                } else {
+                    int numRead;
+                    for (numRead = 0; numRead < SAMPLES_PER_FRAME * 2; ) {
+                        int n = mInputStream.read(mBuf, numRead, SAMPLES_PER_FRAME * 2 - numRead);
+                        if (n == -1) {
+                            mSawInputEOS = true;
+                            break;
+                        }
+                        numRead += n;
+                    }
+                    ByteBuffer buf = mCodec.getInputBuffer(index);
+                    buf.put(mBuf, 0, numRead);
+                    mCodec.queueInputBuffer(index,
+                            0 /* offset */,
+                            numRead,
+                            0 /* presentationTimeUs */,
+                            mSawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0 /* flags */);
+                }
+            }
+
+            // now read encoded data from the encoder (blocking, since we just filled up the
+            // encoder's input with data it should be able to output at least one buffer)
+            while (true) {
+                int index = mCodec.dequeueOutputBuffer(mInfo, -1);
+                if (index >= 0) {
+                    mBufIn = mInfo.size;
+                    ByteBuffer out = mCodec.getOutputBuffer(index);
+                    out.get(mBuf, 0 /* offset */, mBufIn /* length */);
+                    mCodec.releaseOutputBuffer(index,  false /* render */);
+                    if ((mInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                        mSawOutputEOS = true;
+                    }
+                    break;
+                }
+            }
+        }
+
+        if (mBufOut < mBufIn) {
+            // there is data in the buffer
+            if (length > mBufIn - mBufOut) {
+                length = mBufIn - mBufOut;
+            }
+            System.arraycopy(mBuf, mBufOut, b, offset, length);
+            mBufOut += length;
+            return length;
+        }
+
+        if (mSawInputEOS && mSawOutputEOS) {
+            // no more data available in buffer, codec or input stream
+            return -1;
+        }
+
+        // caller should try again
+        return 0;
+    }
+
+    @Override
+    public void close() throws IOException {
+        try {
+            if (mInputStream != null) {
+                mInputStream.close();
+            }
+        } finally {
+            mInputStream = null;
+            try {
+                if (mCodec != null) {
+                    mCodec.release();
+                }
+            } finally {
+                mCodec = null;
+            }
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        if (mCodec != null) {
+            Log.w(TAG, "AmrInputStream wasn't closed");
+            mCodec.release();
+        }
+    }
+}
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 957c2d6..81db37e 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -109,13 +109,13 @@
     private IMediaBrowserServiceCallbacks mServiceCallbacks;
 
     /**
-     * Creates a media browser for the specified media browse service.
+     * Creates a media browser for the specified media browser service.
      *
      * @param context The context.
-     * @param serviceComponent The component name of the media browse service.
+     * @param serviceComponent The component name of the media browser service.
      * @param callback The connection callback.
      * @param rootHints An optional bundle of service-specific arguments to send
-     * to the media browse service when connecting and retrieving the root id
+     * to the media browser service when connecting and retrieving the root id
      * for browsing, or null if none. The contents of this bundle may affect
      * the information returned when browsing.
      * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_RECENT
@@ -140,7 +140,7 @@
     }
 
     /**
-     * Connects to the media browse service.
+     * Connects to the media browser service.
      * <p>
      * The connection callback specified in the constructor will be invoked
      * when the connection completes or fails.
@@ -206,7 +206,7 @@
     }
 
     /**
-     * Disconnects from the media browse service.
+     * Disconnects from the media browser service.
      * After this, no more callbacks will be received.
      */
     public void disconnect() {
@@ -362,7 +362,7 @@
      *
      * @param parentId The id of the parent media item whose list of children
      *            will be subscribed.
-     * @param options A bundle of service-specific arguments to send to the media
+     * @param options The bundle of service-specific arguments to send to the media
      *            browse service. The contents of this bundle may affect the
      *            information returned when browsing.
      * @param callback The callback to receive the list of children.
@@ -370,7 +370,7 @@
     public void subscribe(@NonNull String parentId, @NonNull Bundle options,
             @NonNull SubscriptionCallback callback) {
         if (options == null) {
-            throw new IllegalArgumentException("options are null");
+            throw new IllegalArgumentException("options cannot be null");
         }
         subscribeInternal(parentId, new Bundle(options), callback);
     }
@@ -398,11 +398,11 @@
      *
      * @param parentId The id of the parent media item whose list of children
      *            will be unsubscribed.
-     * @param callback A callback sent to the media browse service to subscribe.
+     * @param callback A callback sent to the media browser service to subscribe.
      */
     public void unsubscribe(@NonNull String parentId, @NonNull SubscriptionCallback callback) {
         if (callback == null) {
-            throw new IllegalArgumentException("callback is null");
+            throw new IllegalArgumentException("callback cannot be null");
         }
         unsubscribeInternal(parentId, callback);
     }
@@ -417,10 +417,10 @@
      */
     public void getItem(final @NonNull String mediaId, @NonNull final ItemCallback cb) {
         if (TextUtils.isEmpty(mediaId)) {
-            throw new IllegalArgumentException("mediaId is empty.");
+            throw new IllegalArgumentException("mediaId cannot be empty.");
         }
         if (cb == null) {
-            throw new IllegalArgumentException("cb is null.");
+            throw new IllegalArgumentException("cb cannot be null.");
         }
         if (mState != CONNECT_STATE_CONNECTED) {
             Log.i(TAG, "Not connected, unable to retrieve the MediaItem.");
@@ -451,7 +451,7 @@
         try {
             mServiceBinder.getMediaItem(mediaId, receiver, mServiceCallbacks);
         } catch (RemoteException e) {
-            Log.i(TAG, "Remote error getting media item.");
+            Log.i(TAG, "Remote error getting media item.", e);
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
@@ -461,13 +461,74 @@
         }
     }
 
+    /**
+     * Searches {@link MediaItem media items} from the connected service. Not
+     * all services may support this, and {@link SearchCallback#onError} will be
+     * called if not implemented.
+     *
+     * @param query The search query that contains keywords separated by space. Should not be
+     *            an empty string.
+     * @param extras The bundle of service-specific arguments to send to the media browser
+     *            service. The contents of this bundle may affect the search result.
+     * @param callback The callback to receive the search result.
+     */
+    public void search(@NonNull final String query, final Bundle extras, SearchCallback callback) {
+        if (TextUtils.isEmpty(query)) {
+            throw new IllegalArgumentException("query cannot be empty.");
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("callback cannot be null.");
+        }
+        if (mState != CONNECT_STATE_CONNECTED) {
+            Log.i(TAG, "Not connected, unable to search.");
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onError(query, extras);
+                }
+            });
+            return;
+        }
+        ResultReceiver receiver = new ResultReceiver(mHandler) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                if (resultCode != 0 || resultData == null
+                        || !resultData.containsKey(MediaBrowserService.KEY_SEARCH_RESULTS)) {
+                    callback.onError(query, extras);
+                    return;
+                }
+                Parcelable[] items = resultData.getParcelableArray(
+                        MediaBrowserService.KEY_SEARCH_RESULTS);
+                List<MediaItem> results = null;
+                if (items != null) {
+                    results = new ArrayList<>();
+                    for (Parcelable item : items) {
+                        results.add((MediaItem) item);
+                    }
+                }
+                callback.onSearchResult(query, extras, results);
+            }
+        };
+        try {
+            mServiceBinder.search(query, extras, receiver, mServiceCallbacks);
+        } catch (RemoteException e) {
+            Log.i(TAG, "Remote error getting media item.", e);
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onError(query, extras);
+                }
+            });
+        }
+    }
+
     private void subscribeInternal(String parentId, Bundle options, SubscriptionCallback callback) {
         // Check arguments.
         if (TextUtils.isEmpty(parentId)) {
-            throw new IllegalArgumentException("parentId is empty.");
+            throw new IllegalArgumentException("parentId cannot be empty.");
         }
         if (callback == null) {
-            throw new IllegalArgumentException("callback is null");
+            throw new IllegalArgumentException("callback cannot be null");
         }
         // Update or create the subscription.
         Subscription sub = mSubscriptions.get(parentId);
@@ -497,7 +558,7 @@
     private void unsubscribeInternal(String parentId, SubscriptionCallback callback) {
         // Check arguments.
         if (TextUtils.isEmpty(parentId)) {
-            throw new IllegalArgumentException("parentId is empty.");
+            throw new IllegalArgumentException("parentId cannot be empty.");
         }
 
         Subscription sub = mSubscriptions.get(parentId);
@@ -712,7 +773,9 @@
     }
 
     /**
-     * A class with information on a single media item for use in browsing media.
+     * A class with information on a single media item for use in browsing/searching media.
+     * MediaItems are application dependent so we cannot guarantee that they contain the
+     * right values.
      */
     public static class MediaItem implements Parcelable {
         private final int mFlags;
@@ -830,7 +893,7 @@
          * Returns the media id in the {@link MediaDescription} for this item.
          * @see android.media.MediaMetadata#METADATA_KEY_MEDIA_ID
          */
-        public @NonNull String getMediaId() {
+        public @Nullable String getMediaId() {
             return mDescription.getMediaId();
         }
     }
@@ -882,7 +945,7 @@
          *
          * @param parentId The media id of the parent media item.
          * @param children The children which were loaded.
-         * @param options A bundle of service-specific arguments sent to the media
+         * @param options The bundle of service-specific arguments sent to the media
          *            browse service. The contents of this bundle may affect the
          *            information returned when browsing.
          */
@@ -912,8 +975,8 @@
          *
          * @param parentId The media id of the parent media item whose children could
          *            not be loaded.
-         * @param options A bundle of service-specific arguments sent to the media
-         *            browse service.
+         * @param options The bundle of service-specific arguments sent to the media
+         *            browser service.
          */
         public void onError(@NonNull String parentId, @NonNull Bundle options) {
         }
@@ -924,7 +987,7 @@
      */
     public static abstract class ItemCallback {
         /**
-         * Called when the item has been returned by the browser service.
+         * Called when the item has been returned by the connected service.
          *
          * @param item The item that was returned or null if it doesn't exist.
          */
@@ -932,11 +995,38 @@
         }
 
         /**
-         * Called when the item doesn't exist or there was an error retrieving it.
+         * Called there was an error retrieving it or the connected service doesn't support
+         * {@link #getItem}.
          *
-         * @param itemId The media id of the media item which could not be loaded.
+         * @param mediaId The media id of the media item which could not be loaded.
          */
-        public void onError(@NonNull String itemId) {
+        public void onError(@NonNull String mediaId) {
+        }
+    }
+
+    /**
+     * Callback for receiving the result of {@link #search}.
+     */
+    public static abstract class SearchCallback {
+        /**
+         * Called when the {@link #search} finished successfully.
+         *
+         * @param query The search query sent for the search request to the connected service.
+         * @param extras The bundle of service-specific arguments sent to the connected service.
+         * @param items The list of media items which contains the search result.
+         */
+        public void onSearchResult(@NonNull String query, Bundle extras,
+                @NonNull List<MediaItem> items) {
+        }
+
+        /**
+         * Called when an error happens while {@link #search} or the connected service doesn't
+         * support {@link #search}.
+         *
+         * @param query The search query sent for the search request to the connected service.
+         * @param extras The bundle of service-specific arguments sent to the connected service.
+         */
+        public void onError(@NonNull String query, Bundle extras) {
         }
     }
 
diff --git a/media/java/android/service/media/IMediaBrowserService.aidl b/media/java/android/service/media/IMediaBrowserService.aidl
index 84f41f6..e95154f 100644
--- a/media/java/android/service/media/IMediaBrowserService.aidl
+++ b/media/java/android/service/media/IMediaBrowserService.aidl
@@ -19,8 +19,10 @@
 
     void addSubscriptionDeprecated(String uri, IMediaBrowserServiceCallbacks callbacks);
     void removeSubscriptionDeprecated(String uri, IMediaBrowserServiceCallbacks callbacks);
-
     void getMediaItem(String uri, in ResultReceiver cb, IMediaBrowserServiceCallbacks callbacks);
+    void search(String query, in Bundle extras, in ResultReceiver cb,
+            IMediaBrowserServiceCallbacks callbacks);
+
     void addSubscription(String uri, in IBinder token, in Bundle options,
             IMediaBrowserServiceCallbacks callbacks);
     void removeSubscription(String uri, in IBinder token, IMediaBrowserServiceCallbacks callbacks);
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index a19e347..16847c1 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -52,9 +52,9 @@
 import java.util.List;
 
 /**
- * Base class for media browse services.
+ * Base class for media browser services.
  * <p>
- * Media browse services enable applications to browse media content provided by an application
+ * Media browser services enable applications to browse media content provided by an application
  * and ask the application to start playing it. They may also be used to control content that
  * is already playing by way of a {@link MediaSession}.
  * </p>
@@ -85,18 +85,27 @@
 
     /**
      * A key for passing the MediaItem to the ResultReceiver in getItem.
-     *
      * @hide
      */
     public static final String KEY_MEDIA_ITEM = "media_item";
 
-    private static final int RESULT_FLAG_OPTION_NOT_HANDLED = 0x00000001;
-    private static final int RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED = 0x00000002;
+    /**
+     * A key for passing the list of MediaItems to the ResultReceiver in search.
+     * @hide
+     */
+    public static final String KEY_SEARCH_RESULTS = "search_results";
+
+    private static final int RESULT_FLAG_OPTION_NOT_HANDLED = 1 << 0;
+    private static final int RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED = 1 << 1;
+    private static final int RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED = 1 << 2;
+
+    private static final int RESULT_ERROR = -1;
+    private static final int RESULT_OK = 0;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag=true, value = { RESULT_FLAG_OPTION_NOT_HANDLED,
-            RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED })
+            RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED, RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED })
     private @interface ResultFlags { }
 
     private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>();
@@ -307,10 +316,6 @@
         @Override
         public void getMediaItem(final String mediaId, final ResultReceiver receiver,
                 final IMediaBrowserServiceCallbacks callbacks) {
-            if (TextUtils.isEmpty(mediaId) || receiver == null) {
-                return;
-            }
-
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
@@ -324,6 +329,23 @@
                 }
             });
         }
+
+        @Override
+        public void search(final String query, Bundle extras, ResultReceiver receiver,
+                final IMediaBrowserServiceCallbacks callbacks) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    final IBinder b = callbacks.asBinder();
+                    ConnectionRecord connection = mConnections.get(b);
+                    if (connection == null) {
+                        Log.w(TAG, "search for callback that isn't registered query=" + query);
+                        return;
+                    }
+                    performSearch(query, extras, connection, receiver);
+                }
+            });
+        }
     }
 
     @Override
@@ -358,7 +380,7 @@
      * @param clientUid The uid of the application which is requesting access to
      *            browse media.
      * @param rootHints An optional bundle of service-specific arguments to send
-     *            to the media browse service when connecting and retrieving the
+     *            to the media browser service when connecting and retrieving the
      *            root id for browsing, or null if none. The contents of this
      *            bundle may affect the information returned when browsing.
      * @return The {@link BrowserRoot} for accessing this app's content or null.
@@ -412,8 +434,8 @@
      * @param parentId The id of the parent media item whose children are to be
      *            queried.
      * @param result The Result to send the list of children to.
-     * @param options A bundle of service-specific arguments sent from the media
-     *            browse. The information returned through the result should be
+     * @param options The bundle of service-specific arguments sent from the media
+     *            browser. The information returned through the result should be
      *            affected by the contents of this bundle.
      */
     public void onLoadChildren(@NonNull String parentId,
@@ -450,6 +472,32 @@
     }
 
     /**
+     * Called to get the search result.
+     * <p>
+     * Implementations must call {@link Result#sendResult result.sendResult}. If
+     * the search will be an expensive operation {@link Result#detach result.detach}
+     * may be called before returning from this function, and then {@link Result#sendResult
+     * result.sendResult} called when the search has been completed.
+     * </p><p>
+     * In case there are no search results, call {@link Result#sendResult} with an empty list.
+     * In case there are some errors happened, call {@link Result#sendResult result.sendResult}
+     * with {@code null}, which will invoke {@link MediaBrowser.SearchCallback#onError}.
+     * </p><p>
+     * The default implementation will invoke {@link MediaBrowser.SearchCallback#onError}.
+     * </p>
+     *
+     * @param query The search query sent from the media browser. It contains keywords separated
+     *            by space.
+     * @param extras The bundle of service-specific arguments sent from the media browser.
+     * @param result The {@link Result} to send the search result.
+     */
+    public void onSearch(@NonNull String query, Bundle extras,
+            Result<List<MediaBrowser.MediaItem>> result) {
+        result.setFlags(RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED);
+        result.sendResult(null);
+    }
+
+    /**
      * Call to set the media session.
      * <p>
      * This should be called as soon as possible during the service's startup.
@@ -531,8 +579,8 @@
      *
      * @param parentId The id of the parent media item whose
      *            children changed.
-     * @param options A bundle of service-specific arguments to send
-     *            to the media browse. The contents of this bundle may
+     * @param options The bundle of service-specific arguments to send
+     *            to the media browser. The contents of this bundle may
      *            contain the information about the change.
      */
     public void notifyChildrenChanged(@NonNull String parentId, @NonNull Bundle options) {
@@ -705,12 +753,12 @@
             @Override
             void onResultSent(MediaBrowser.MediaItem item, @ResultFlags int flag) {
                 if ((flag & RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED) != 0) {
-                    receiver.send(-1, null);
+                    receiver.send(RESULT_ERROR, null);
                     return;
                 }
                 Bundle bundle = new Bundle();
                 bundle.putParcelable(KEY_MEDIA_ITEM, item);
-                receiver.send(0, bundle);
+                receiver.send(RESULT_OK, bundle);
             }
         };
 
@@ -724,6 +772,34 @@
         }
     }
 
+    private void performSearch(String query, Bundle extras, final ConnectionRecord connection,
+            final ResultReceiver receiver) {
+        final Result<List<MediaBrowser.MediaItem>> result =
+                new Result<List<MediaBrowser.MediaItem>>(query) {
+            @Override
+            void onResultSent(List<MediaBrowser.MediaItem> items, @ResultFlags int flag) {
+                if ((flag & RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED) != 0
+                        || items == null) {
+                    receiver.send(RESULT_ERROR, null);
+                    return;
+                }
+                Bundle bundle = new Bundle();
+                bundle.putParcelableArray(KEY_SEARCH_RESULTS,
+                        items.toArray(new MediaBrowser.MediaItem[0]));
+                receiver.send(RESULT_OK, bundle);
+            }
+        };
+
+        mCurConnection = connection;
+        onSearch(query, extras, result);
+        mCurConnection = null;
+
+        if (!result.isDone()) {
+            throw new IllegalStateException("onSearch must call detach() or sendResult()"
+                    + " before returning for query=" + query);
+        }
+    }
+
     /**
      * Contains information that the browser service needs to send to the client
      * when first connected.
diff --git a/media/mca/filterfw/native/core/shader_program.cpp b/media/mca/filterfw/native/core/shader_program.cpp
index 1e573fb..d460512 100644
--- a/media/mca/filterfw/native/core/shader_program.cpp
+++ b/media/mca/filterfw/native/core/shader_program.cpp
@@ -1028,7 +1028,11 @@
   attrib.values = data_cpy;
   attrib.owned_data = data_cpy; // Marks this for deletion later on
 
-  return StoreAttribute(attrib);
+  if (StoreAttribute(attrib))
+    return true;
+  // If storing this failed, then it won't be deleted on its own.
+  delete[] data_cpy;
+  return false;
 }
 
 bool ShaderProgram::StoreAttribute(VertexAttrib attrib) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkIntegrationTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkIntegrationTestRunner.java
index 7751fcc..6511cc8 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkIntegrationTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkIntegrationTestRunner.java
@@ -38,7 +38,7 @@
 
     private static final String TAG = "MediaFrameworkIntegrationTestRunner";
 
-    public static int mCameraId = 0;
+    public static String mCameraId = "0";
 
     @Override
     public TestSuite getAllTests() {
@@ -62,7 +62,7 @@
             try {
                 Log.v(TAG,
                         String.format("Reading camera_id from icicle: '%s'", cameraId));
-                mCameraId = Integer.parseInt(cameraId);
+                mCameraId = cameraId;
             }
             catch (NumberFormatException e) {
                 Log.e(TAG, String.format("Failed to convert camera_id to integer"));
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index 4f2fdff..fcb861c 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -116,8 +116,8 @@
     @SmallTest
     public void testSupportsCamera2Api() throws Exception {
         for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
-
-            boolean supports = mUtils.getCameraService().supportsCameraApi(cameraId, API_VERSION_2);
+            boolean supports = mUtils.getCameraService().supportsCameraApi(
+                String.valueOf(cameraId), API_VERSION_2);
 
             Log.v(TAG, "Camera " + cameraId + " supports api2: " + supports);
         }
@@ -128,7 +128,8 @@
     public void testSupportsCamera1Api() throws Exception {
         for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
 
-            boolean supports = mUtils.getCameraService().supportsCameraApi(cameraId, API_VERSION_1);
+            boolean supports = mUtils.getCameraService().supportsCameraApi(
+                String.valueOf(cameraId), API_VERSION_1);
             assertTrue(
                     "Camera service returned false when queried if it supports camera1 api " +
                     " for camera ID " + cameraId, supports);
@@ -285,7 +286,7 @@
 
             ICameraDeviceUser cameraUser =
                     mUtils.getCameraService().connectDevice(
-                        dummyCallbacks, cameraId,
+                        dummyCallbacks, String.valueOf(cameraId),
                         clientPackageName,
                         ICameraService.USE_CALLING_UID);
             assertNotNull(String.format("Camera %s was null", cameraId), cameraUser);
@@ -298,9 +299,9 @@
 
     static class DummyCameraServiceListener extends ICameraServiceListener.Stub {
         @Override
-        public void onStatusChanged(int status, int cameraId)
+        public void onStatusChanged(int status, String cameraId)
                 throws RemoteException {
-            Log.v(TAG, String.format("Camera %d has status changed to 0x%x", cameraId, status));
+            Log.v(TAG, String.format("Camera %s has status changed to 0x%x", cameraId, status));
         }
         public void onTorchStatusChanged(int status, String cameraId)
                 throws RemoteException {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 832363c..9a78544 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -66,7 +66,7 @@
     private static final int DEFAULT_IMAGE_HEIGHT = 480;
     private static final int MAX_NUM_IMAGES = 5;
 
-    private int mCameraId;
+    private String mCameraId;
     private ICameraDeviceUser mCameraUser;
     private CameraBinderTestUtils mUtils;
     private ICameraDeviceCallbacks.Stub mMockCb;
diff --git a/packages/BackupRestoreConfirmation/res/values-si-rLK/strings.xml b/packages/BackupRestoreConfirmation/res/values-si-rLK/strings.xml
index aaeaf04..cbc130d 100644
--- a/packages/BackupRestoreConfirmation/res/values-si-rLK/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-si-rLK/strings.xml
@@ -29,7 +29,7 @@
     <string name="device_encryption_backup_text" msgid="5866590762672844664">"කරුණාකර ඔබගේ උපාංගයේ සංකේතන මුරපදය පහත ඇතුලත් කරන්න. සංරක්ෂිත උපස්ථ සංකේතනය කිරීමට මෙය භාවිත කළ හැක."</string>
     <string name="backup_enc_password_text" msgid="4981585714795233099">"කරුණාකර සියලු උපස්ථ දත්ත සංකේතනය කිරීම සඳහා භාවිතයට මුරපදයක් ඇතුළත් කරන්න. මෙය හිස්ව තැබුවොත්, ඔබගේ වර්තමාන උපස්ථ මුරපදය භාවිත වෙයි:"</string>
     <string name="backup_enc_password_optional" msgid="1350137345907579306">"සියලු උපස්ථ දත්ත සංකේතනය කිරීමට ඔබ අදහස් කරන්නේ නම්, මුරපදය පහලින් ඇතුලත් කරන්න:"</string>
-    <string name="backup_enc_password_required" msgid="7889652203371654149">"ඔබගේ උපාංගය සංකේතනය කර තිබෙන නිසා, ඔබගේ උපස්ථය සංකේතනය ඔබට අවශ්‍ය වී තිබේ. කරුණාකර මුරපදය පහළින් එකතු කරන්න:"</string>
+    <string name="backup_enc_password_required" msgid="7889652203371654149">"ඔබගේ උපාංගය සංකේතනය කර තිබෙන නිසා, ඔබගේ උපස්ථය සංකේතනය ඔබට අවශ්‍ය වී තිබේ. මුරපදය පහළින් එක් කරන්න:"</string>
     <string name="restore_enc_password_text" msgid="6140898525580710823">"යළි පිහිටුවන දත්ත සංකේතනය කරන ලද ඒවානම්, කරුණාකර මුරපදය පහලින් ඇතුල් කරන්න:"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"උපස්ථ කිරීම ආරම්භ කරමින්..."</string>
     <string name="toast_backup_ended" msgid="3818080769548726424">"උපස්ථය අවසන්"</string>
diff --git a/packages/PrintSpooler/res/values-si-rLK/strings.xml b/packages/PrintSpooler/res/values-si-rLK/strings.xml
index ffff8e0..e661f93 100644
--- a/packages/PrintSpooler/res/values-si-rLK/strings.xml
+++ b/packages/PrintSpooler/res/values-si-rLK/strings.xml
@@ -52,7 +52,7 @@
     <string name="add_print_service_label" msgid="5356702546188981940">"සේවාව එක් කිරීම"</string>
     <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"සෙවීම් කොටුව පෙන්වන ලදී"</string>
     <string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"සෙවීම් කොටුව සඟවන ලදී"</string>
-    <string name="print_add_printer" msgid="1088656468360653455">"මුද්‍රණ යන්ත්‍ර එකතු කරන්න"</string>
+    <string name="print_add_printer" msgid="1088656468360653455">"මුද්‍රණ යන්ත්‍ර එක් කරන්න"</string>
     <string name="print_select_printer" msgid="7388760939873368698">"මුද්‍රකය තේරීම"</string>
     <string name="print_forget_printer" msgid="5035287497291910766">"මුද්‍රකය අමතක කිරීම"</string>
     <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
diff --git a/packages/SettingsLib/res/drawable/ic_menu.xml b/packages/SettingsLib/res/drawable/ic_menu.xml
index 910a3d0..b77db08 100644
--- a/packages/SettingsLib/res/drawable/ic_menu.xml
+++ b/packages/SettingsLib/res/drawable/ic_menu.xml
@@ -17,7 +17,8 @@
         android:width="24.0dp"
         android:height="24.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M3.0,18.0l18.0,0.0l0.0,-2.0L3.0,16.0l0.0,2.0zm0.0,-5.0l18.0,0.0l0.0,-2.0L3.0,11.0l0.0,2.0zm0.0,-7.0l0.0,2.0l18.0,0.0L21.0,6.0L3.0,6.0z"/>
diff --git a/packages/SettingsLib/src/com/android/settingslib/widget/AnimatedImageView.java b/packages/SettingsLib/src/com/android/settingslib/widget/AnimatedImageView.java
index f5e39be..a8cd655 100644
--- a/packages/SettingsLib/src/com/android/settingslib/widget/AnimatedImageView.java
+++ b/packages/SettingsLib/src/com/android/settingslib/widget/AnimatedImageView.java
@@ -55,7 +55,7 @@
 
     private void updateAnimating() {
         if (mDrawable != null) {
-            if (isShown() && mAnimating) {
+            if (getVisibility() == View.VISIBLE && mAnimating) {
                 mDrawable.start();
             } else {
                 mDrawable.stop();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index c2161ae..aee6c3c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -839,16 +839,19 @@
         if (state == DetailedState.CONNECTED) {
             IWifiManager wifiManager = IWifiManager.Stub.asInterface(
                     ServiceManager.getService(Context.WIFI_SERVICE));
-            Network nw;
+            NetworkCapabilities nc = null;
 
             try {
-                nw = wifiManager.getCurrentNetwork();
-            } catch (RemoteException e) {
-                nw = null;
-            }
-            NetworkCapabilities nc = cm.getNetworkCapabilities(nw);
-            if (nc != null && !nc.hasCapability(nc.NET_CAPABILITY_VALIDATED)) {
-                return context.getString(R.string.wifi_connected_no_internet);
+                nc = cm.getNetworkCapabilities(wifiManager.getCurrentNetwork());
+            } catch (RemoteException e) {}
+
+            if (nc != null) {
+                if (nc.hasCapability(nc.NET_CAPABILITY_CAPTIVE_PORTAL)) {
+                    return context.getString(
+                        com.android.internal.R.string.network_available_sign_in);
+                } else if (!nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
+                    return context.getString(R.string.wifi_connected_no_internet);
+                }
             }
         }
         if (state == null) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
new file mode 100644
index 0000000..2c9c868
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 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.settingslib.widget;
+
+import android.app.Activity;
+import android.graphics.drawable.AnimatedRotateDrawable;
+import android.view.View;
+import com.android.settingslib.TestConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import static com.google.common.truth.Truth.assertThat;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class AnimatedImageViewTest {
+    private AnimatedImageView mAnimatedImageView;
+
+    @Before
+    public void setUp() {
+        Activity activity = Robolectric.setupActivity(Activity.class);
+        mAnimatedImageView = new AnimatedImageView(activity);
+        mAnimatedImageView.setImageDrawable(new AnimatedRotateDrawable());
+    }
+
+    @Test
+    public void testAnimation_ViewVisible_AnimationRunning() {
+        mAnimatedImageView.setVisibility(View.VISIBLE);
+        mAnimatedImageView.setAnimating(true);
+        AnimatedRotateDrawable drawable = (AnimatedRotateDrawable) mAnimatedImageView.getDrawable();
+        assertThat(drawable.isRunning()).isTrue();
+    }
+
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 88ee33c..c149876 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -57,6 +57,7 @@
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.provider.Settings;
+import android.provider.Settings.Global;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -2175,7 +2176,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 135;
+            private static final int SETTINGS_VERSION = 136;
 
             private final int mUserId;
 
@@ -2553,6 +2554,30 @@
                     currentVersion = 135;
                 }
 
+                if (currentVersion == 135) {
+                    // Version 135: Migrating the NETWORK_SCORER_APP setting to the
+                    // NETWORK_RECOMMENDATIONS_ENABLED setting.
+                    if (userId == UserHandle.USER_SYSTEM) {
+                        final SettingsState globalSettings = getGlobalSettingsLocked();
+                        Setting currentSetting = globalSettings.getSettingLocked(
+                            Global.NETWORK_SCORER_APP);
+                        if (!currentSetting.isNull()) {
+                            // A scorer was set so enable recommendations.
+                            globalSettings.insertSettingLocked(
+                                Global.NETWORK_RECOMMENDATIONS_ENABLED,
+                                "1",
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+
+                            // and clear the scorer setting since it's no longer needed.
+                            globalSettings.insertSettingLocked(
+                                Global.NETWORK_SCORER_APP,
+                                null,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+                    }
+                    currentVersion = 136;
+                }
+
                 if (currentVersion != newVersion) {
                     Slog.wtf("SettingsProvider", "warning: upgrading settings database to version "
                             + newVersion + " left it at "
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index df71ced..386fbc9 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -560,7 +560,7 @@
 
     @Override
     public void interrupt(int userId) {
-        CopyOnWriteArrayList<Service> services;
+        List<IAccessibilityServiceClient> interfacesToInterrupt;
         synchronized (mLock) {
             // We treat calls from a profile as if made by its parent as profiles
             // share the accessibility state of the parent. The call below
@@ -571,15 +571,24 @@
             if (resolvedUserId != mCurrentUserId) {
                 return;
             }
-            services = getUserStateLocked(resolvedUserId).mBoundServices;
+            List<Service> services = getUserStateLocked(resolvedUserId).mBoundServices;
+            int numServices = services.size();
+            interfacesToInterrupt = new ArrayList<>(numServices);
+            for (int i = 0; i < numServices; i++) {
+                Service service = services.get(i);
+                IBinder a11yServiceBinder = service.mService;
+                IAccessibilityServiceClient a11yServiceInterface = service.mServiceInterface;
+                if ((a11yServiceBinder != null) && (a11yServiceInterface != null)) {
+                    interfacesToInterrupt.add(a11yServiceInterface);
+                }
+            }
         }
-        for (int i = 0, count = services.size(); i < count; i++) {
-            Service service = services.get(i);
+        for (int i = 0, count = interfacesToInterrupt.size(); i < count; i++) {
             try {
-                service.mServiceInterface.onInterrupt();
+                interfacesToInterrupt.get(i).onInterrupt();
             } catch (RemoteException re) {
-                Slog.e(LOG_TAG, "Error during sending interrupt request to "
-                    + service.mService, re);
+                Slog.e(LOG_TAG, "Error sending interrupt request to "
+                        + interfacesToInterrupt.get(i), re);
             }
         }
     }
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index b0c5603..d3110a4 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -105,6 +105,7 @@
     private static final int MESSAGE_USER_UNLOCKED = 301;
     private static final int MESSAGE_ADD_PROXY_DELAYED = 400;
     private static final int MESSAGE_BIND_PROFILE_SERVICE = 401;
+
     private static final int MAX_SAVE_RETRIES = 3;
     private static final int MAX_ERROR_RESTART_RETRIES = 6;
 
@@ -229,7 +230,8 @@
                     } finally {
                         mBluetoothLock.readLock().unlock();
                     }
-                    Slog.d(TAG, "Airplane Mode change - current state: " + st);
+                    Slog.d(TAG, "Airplane Mode change - current state: " +
+                              BluetoothAdapter.nameForState(st));
 
                     if (isAirplaneModeOn()) {
                         // Clear registered LE apps to force shut-off
@@ -426,6 +428,10 @@
     public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
                                                 "Need BLUETOOTH permission");
+        if (callback == null) {
+          Slog.w(TAG, "registerStateChangeCallback: Callback is null!");
+          return;
+        }
         Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK);
         msg.obj = callback;
         mHandler.sendMessage(msg);
@@ -434,6 +440,10 @@
     public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
                                                 "Need BLUETOOTH permission");
+        if (callback == null) {
+          Slog.w(TAG, "unregisterStateChangeCallback: Callback is null!");
+          return;
+        }
         Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK);
         msg.obj = callback;
         mHandler.sendMessage(msg);
@@ -460,7 +470,7 @@
     public int getState() {
         if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
                 (!checkIfCallerIsForegroundUser())) {
-            Slog.w(TAG, "getState(): not allowed for non-active and non system user");
+            Slog.w(TAG, "getState(): report OFF for non-active and non system user");
             return BluetoothAdapter.STATE_OFF;
         }
 
@@ -712,7 +722,8 @@
 
         if (DBG) {
             Slog.d(TAG,"enable():  mBluetooth =" + mBluetooth +
-                    " mBinding = " + mBinding + " mState = " + mState);
+                    " mBinding = " + mBinding + " mState = " +
+                    BluetoothAdapter.nameForState(mState));
         }
 
         synchronized(mReceiver) {
@@ -800,7 +811,7 @@
     public void unbindAndFinish() {
         if (DBG) {
             Slog.d(TAG,"unbindAndFinish(): " + mBluetooth +
-                " mBinding = " + mBinding);
+                " mBinding = " + mBinding + " mUnbinding = " + mUnbinding);
         }
 
         try {
@@ -816,16 +827,13 @@
                 } catch (RemoteException re) {
                     Slog.e(TAG, "Unable to unregister BluetoothCallback",re);
                 }
-
-                if (DBG) Slog.d(TAG, "Sending unbind request.");
                 mBluetoothBinder = null;
                 mBluetooth = null;
-                //Unbind
                 mContext.unbindService(mConnection);
                 mUnbinding = false;
                 mBinding = false;
             } else {
-                mUnbinding=false;
+                mUnbinding = false;
             }
             mBluetoothGatt = null;
         } finally {
@@ -1101,7 +1109,6 @@
      * Inform BluetoothAdapter instances that Adapter service is up
      */
     private void sendBluetoothServiceUpCallback() {
-        if (DBG) Slog.d(TAG,"Calling onBluetoothServiceUp callbacks");
         try {
             int n = mCallbacks.beginBroadcast();
             Slog.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
@@ -1120,7 +1127,6 @@
      * Inform BluetoothAdapter instances that Adapter service is down
      */
     private void sendBluetoothServiceDownCallback() {
-        if (DBG) Slog.d(TAG,"Calling onBluetoothServiceDown callbacks");
         try {
             int n = mCallbacks.beginBroadcast();
             Slog.d(TAG,"Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
@@ -1192,34 +1198,33 @@
     }
 
     private class BluetoothServiceConnection implements ServiceConnection {
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (DBG) Slog.d(TAG, "BluetoothServiceConnection: " + className.getClassName());
+        public void onServiceConnected(ComponentName componentName, IBinder service) {
+            String name = componentName.getClassName();
+            if (DBG) Slog.d(TAG, "BluetoothServiceConnection: " + name);
             Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
-            // TBD if (className.getClassName().equals(IBluetooth.class.getName())) {
-            if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) {
+            if (name.equals("com.android.bluetooth.btservice.AdapterService")) {
                 msg.arg1 = SERVICE_IBLUETOOTH;
-                // } else if (className.getClassName().equals(IBluetoothGatt.class.getName())) {
-            } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) {
+            } else if (name.equals("com.android.bluetooth.gatt.GattService")) {
                 msg.arg1 = SERVICE_IBLUETOOTHGATT;
             } else {
-                Slog.e(TAG, "Unknown service connected: " + className.getClassName());
+                Slog.e(TAG, "Unknown service connected: " + name);
                 return;
             }
             msg.obj = service;
             mHandler.sendMessage(msg);
         }
 
-        public void onServiceDisconnected(ComponentName className) {
-            // Called if we unexpected disconnected.
-            if (DBG) Slog.d(TAG, "BluetoothServiceConnection, disconnected: " +
-                           className.getClassName());
+        public void onServiceDisconnected(ComponentName componentName) {
+            // Called if we unexpectedly disconnect.
+            String name = componentName.getClassName();
+            if (DBG) Slog.d(TAG, "BluetoothServiceConnection, disconnected: " + name);
             Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);
-            if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) {
+            if (name.equals("com.android.bluetooth.btservice.AdapterService")) {
                 msg.arg1 = SERVICE_IBLUETOOTH;
-            } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) {
+            } else if (name.equals("com.android.bluetooth.gatt.GattService")) {
                 msg.arg1 = SERVICE_IBLUETOOTHGATT;
             } else {
-                Slog.e(TAG, "Unknown service disconnected: " + className.getClassName());
+                Slog.e(TAG, "Unknown service disconnected: " + name);
                 return;
             }
             mHandler.sendMessage(msg);
@@ -1237,7 +1242,6 @@
 
         @Override
         public void handleMessage(Message msg) {
-            if (DBG) Slog.d (TAG, "Message: " + msg.what);
             switch (msg.what) {
                 case MESSAGE_GET_NAME_AND_ADDRESS:
                     if (DBG) Slog.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS");
@@ -1275,7 +1279,7 @@
 
                 case MESSAGE_ENABLE:
                     if (DBG) {
-                        Slog.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth);
+                        Slog.d(TAG, "MESSAGE_ENABLE(" + msg.arg1 + "): mBluetooth = " + mBluetooth);
                     }
                     mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
                     mEnable = true;
@@ -1325,6 +1329,7 @@
                     break;
 
                 case MESSAGE_DISABLE:
+                    if (DBG) Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth);
                     mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
                     if (mEnable && mBluetooth != null) {
                         waitForOnOff(true, false);
@@ -1340,31 +1345,25 @@
                 case MESSAGE_REGISTER_ADAPTER:
                 {
                     IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
-                    boolean added = mCallbacks.register(callback);
-                    Slog.d(TAG,"Added callback: " +  (callback == null? "null": callback)  +":" +added );
-                }
+                    mCallbacks.register(callback);
                     break;
+                }
                 case MESSAGE_UNREGISTER_ADAPTER:
                 {
                     IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
-                    boolean removed = mCallbacks.unregister(callback);
-                    Slog.d(TAG,"Removed callback: " +  (callback == null? "null": callback)  +":" + removed);
+                    mCallbacks.unregister(callback);
                     break;
                 }
                 case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK:
                 {
                     IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
-                    if (callback != null) {
-                        mStateChangeCallbacks.register(callback);
-                    }
+                    mStateChangeCallbacks.register(callback);
                     break;
                 }
                 case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK:
                 {
                     IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
-                    if (callback != null) {
-                        mStateChangeCallbacks.unregister(callback);
-                    }
+                    mStateChangeCallbacks.unregister(callback);
                     break;
                 }
                 case MESSAGE_ADD_PROXY_DELAYED:
@@ -1438,13 +1437,11 @@
                         //Do enable request
                         try {
                             if (mQuietEnable == false) {
-                                if(!mBluetooth.enable()) {
+                                if (!mBluetooth.enable()) {
                                     Slog.e(TAG,"IBluetooth.enable() returned false");
                                 }
-                            }
-                            else
-                            {
-                                if(!mBluetooth.enableNoAutoConnect()) {
+                            } else {
+                                if (!mBluetooth.enableNoAutoConnect()) {
                                     Slog.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
                                 }
                             }
@@ -1462,19 +1459,14 @@
                     }
                     break;
                 }
-                case MESSAGE_TIMEOUT_BIND: {
-                    Slog.e(TAG, "MESSAGE_TIMEOUT_BIND");
-                    mBluetoothLock.writeLock().lock();
-                    mBinding = false;
-                    mBluetoothLock.writeLock().unlock();
-
-                    break;
-                }
                 case MESSAGE_BLUETOOTH_STATE_CHANGE:
                 {
                     int prevState = msg.arg1;
                     int newState = msg.arg2;
-                    if (DBG) Slog.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: prevState = " + prevState + ", newState =" + newState);
+                    if (DBG) {
+                      Slog.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: " + BluetoothAdapter.nameForState(prevState) + " > " +
+                        BluetoothAdapter.nameForState(newState));
+                    }
                     mState = newState;
                     bluetoothStateChangeHandler(prevState, newState);
                     // handle error state transition case from TURNING_ON to OFF
@@ -1514,7 +1506,7 @@
                 }
                 case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
                 {
-                    Slog.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: " + msg.arg1);
+                    Slog.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED(" + msg.arg1 + ")");
                     try {
                         mBluetoothLock.writeLock().lock();
                         if (msg.arg1 == SERVICE_IBLUETOOTH) {
@@ -1525,7 +1517,7 @@
                             mBluetoothGatt = null;
                             break;
                         } else {
-                            Slog.e(TAG, "Bad msg.arg1: " + msg.arg1);
+                            Slog.e(TAG, "Unknown argument for service disconnect!");
                             break;
                         }
                     } finally {
@@ -1562,8 +1554,7 @@
                 }
                 case MESSAGE_RESTART_BLUETOOTH_SERVICE:
                 {
-                    Slog.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE:"
-                        +" Restart IBluetooth service");
+                    Slog.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE");
                     /* Enable without persisting the setting as
                      it doesnt change when IBluetooth
                      service restarts */
@@ -1571,7 +1562,13 @@
                     handleEnable(mQuietEnable);
                     break;
                 }
-
+                case MESSAGE_TIMEOUT_BIND: {
+                    Slog.e(TAG, "MESSAGE_TIMEOUT_BIND");
+                    mBluetoothLock.writeLock().lock();
+                    mBinding = false;
+                    mBluetoothLock.writeLock().unlock();
+                    break;
+                }
                 case MESSAGE_TIMEOUT_UNBIND:
                 {
                     Slog.e(TAG, "MESSAGE_TIMEOUT_UNBIND");
@@ -1657,11 +1654,10 @@
                     } else if (mBinding || mBluetooth != null) {
                         Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED);
                         userMsg.arg2 = 1 + msg.arg2;
-                        // if user is switched when service is being binding
-                        // delay sending MESSAGE_USER_SWITCHED
+                        // if user is switched when service is binding retry after a delay
                         mHandler.sendMessageDelayed(userMsg, USER_SWITCHED_TIME_MS);
                         if (DBG) {
-                            Slog.d(TAG, "delay MESSAGE_USER_SWITCHED " + userMsg.arg2);
+                            Slog.d(TAG, "Retry MESSAGE_USER_SWITCHED " + userMsg.arg2);
                         }
                     }
                     break;
@@ -1762,7 +1758,7 @@
                     parentUser == foregroundUser    ||
                     callingAppId == Process.NFC_UID ||
                     callingAppId == mSystemUiUid;
-            if (DBG) {
+            if (DBG && !valid) {
                 Slog.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid
                     + " callingUser=" + callingUser
                     + " parentUser=" + parentUser
@@ -1775,7 +1771,8 @@
     }
 
     private void sendBleStateChanged(int prevState, int newState) {
-        if (DBG) Slog.d(TAG,"BLE State Change Intent: " + prevState + " -> " + newState);
+        if (DBG) Slog.d(TAG,"Sending BLE State Change: " + BluetoothAdapter.nameForState(prevState) +
+            " > " + BluetoothAdapter.nameForState(newState));
         // Send broadcast message to everyone else
         Intent intent = new Intent(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
         intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
@@ -1786,76 +1783,76 @@
 
     private void bluetoothStateChangeHandler(int prevState, int newState) {
         boolean isStandardBroadcast = true;
-        if (DBG) Slog.d(TAG, "bluetoothStateChangeHandler: " + prevState + " ->  " + newState);
-        if (prevState != newState) {
-            //Notify all proxy objects first of adapter state change
-            if (newState == BluetoothAdapter.STATE_BLE_ON ||
-                    newState == BluetoothAdapter.STATE_OFF) {
-                boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF
-                   && newState == BluetoothAdapter.STATE_BLE_ON);
+        if (prevState == newState) { // No change. Nothing to do.
+            return;
+        }
+        // Notify all proxy objects first of adapter state change
+        if (newState == BluetoothAdapter.STATE_BLE_ON ||
+                newState == BluetoothAdapter.STATE_OFF) {
+            boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF
+               && newState == BluetoothAdapter.STATE_BLE_ON);
 
-                if (newState == BluetoothAdapter.STATE_OFF) {
-                    // If Bluetooth is off, send service down event to proxy objects, and unbind
-                    if (DBG) Slog.d(TAG, "Bluetooth is complete turn off");
-                    sendBluetoothServiceDownCallback();
-                    unbindAndFinish();
-                    sendBleStateChanged(prevState, newState);
-                    // Don't broadcast as it has already been broadcast before
-                    isStandardBroadcast = false;
-
-                } else if (!intermediate_off) {
-                    // connect to GattService
-                    if (DBG) Slog.d(TAG, "Bluetooth is in LE only mode");
-                    if (mBluetoothGatt != null) {
-                        if (DBG) Slog.d(TAG, "Calling BluetoothGattServiceUp");
-                        onBluetoothGattServiceUp();
-                    } else {
-                        if (DBG) Slog.d(TAG, "Binding Bluetooth GATT service");
-                        if (mContext.getPackageManager().hasSystemFeature(
-                                                        PackageManager.FEATURE_BLUETOOTH_LE)) {
-                            Intent i = new Intent(IBluetoothGatt.class.getName());
-                            doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT);
-                        }
-                    }
-                    sendBleStateChanged(prevState, newState);
-                    //Don't broadcase this as std intent
-                    isStandardBroadcast = false;
-
-                } else if (intermediate_off){
-                    if (DBG) Slog.d(TAG, "Intermediate off, back to LE only mode");
-                    // For LE only mode, broadcast as is
-                    sendBleStateChanged(prevState, newState);
-                    sendBluetoothStateCallback(false); // BT is OFF for general users
-                    // Broadcast as STATE_OFF
-                    newState = BluetoothAdapter.STATE_OFF;
-                    sendBrEdrDownCallback();
-                }
-            } else if (newState == BluetoothAdapter.STATE_ON) {
-                boolean isUp = (newState==BluetoothAdapter.STATE_ON);
-                sendBluetoothStateCallback(isUp);
+            if (newState == BluetoothAdapter.STATE_OFF) {
+                // If Bluetooth is off, send service down event to proxy objects, and unbind
+                if (DBG) Slog.d(TAG, "Bluetooth is complete send Service Down");
+                sendBluetoothServiceDownCallback();
+                unbindAndFinish();
                 sendBleStateChanged(prevState, newState);
-
-            } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON ||
-                    newState == BluetoothAdapter.STATE_BLE_TURNING_OFF ) {
-                sendBleStateChanged(prevState, newState);
+                // Don't broadcast as it has already been broadcast before
                 isStandardBroadcast = false;
 
-            } else if (newState == BluetoothAdapter.STATE_TURNING_ON ||
-                    newState == BluetoothAdapter.STATE_TURNING_OFF) {
-                sendBleStateChanged(prevState, newState);
-            }
-
-            if (isStandardBroadcast) {
-                if (prevState == BluetoothAdapter.STATE_BLE_ON) {
-                    // Show prevState of BLE_ON as OFF to standard users
-                    prevState = BluetoothAdapter.STATE_OFF;
+            } else if (!intermediate_off) {
+                // connect to GattService
+                if (DBG) Slog.d(TAG, "Bluetooth is in LE only mode");
+                if (mBluetoothGatt != null) {
+                    if (DBG) Slog.d(TAG, "Calling BluetoothGattServiceUp");
+                    onBluetoothGattServiceUp();
+                } else {
+                    if (DBG) Slog.d(TAG, "Binding Bluetooth GATT service");
+                    if (mContext.getPackageManager().hasSystemFeature(
+                                                    PackageManager.FEATURE_BLUETOOTH_LE)) {
+                        Intent i = new Intent(IBluetoothGatt.class.getName());
+                        doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT);
+                    }
                 }
-                Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
-                intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
-                intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
-                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-                mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM);
+                sendBleStateChanged(prevState, newState);
+                //Don't broadcase this as std intent
+                isStandardBroadcast = false;
+
+            } else if (intermediate_off) {
+                if (DBG) Slog.d(TAG, "Intermediate off, back to LE only mode");
+                // For LE only mode, broadcast as is
+                sendBleStateChanged(prevState, newState);
+                sendBluetoothStateCallback(false); // BT is OFF for general users
+                // Broadcast as STATE_OFF
+                newState = BluetoothAdapter.STATE_OFF;
+                sendBrEdrDownCallback();
             }
+        } else if (newState == BluetoothAdapter.STATE_ON) {
+            boolean isUp = (newState == BluetoothAdapter.STATE_ON);
+            sendBluetoothStateCallback(isUp);
+            sendBleStateChanged(prevState, newState);
+
+        } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON ||
+                newState == BluetoothAdapter.STATE_BLE_TURNING_OFF ) {
+            sendBleStateChanged(prevState, newState);
+            isStandardBroadcast = false;
+
+        } else if (newState == BluetoothAdapter.STATE_TURNING_ON ||
+                newState == BluetoothAdapter.STATE_TURNING_OFF) {
+            sendBleStateChanged(prevState, newState);
+        }
+
+        if (isStandardBroadcast) {
+            if (prevState == BluetoothAdapter.STATE_BLE_ON) {
+                // Show prevState of BLE_ON as OFF to standard users
+                prevState = BluetoothAdapter.STATE_OFF;
+            }
+            Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
+            intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
+            intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM);
         }
     }
 
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c9fd568..7572dfe 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -720,16 +720,6 @@
         mHandler = new InternalHandler(mHandlerThread.getLooper());
         mTrackerHandler = new NetworkStateTrackerHandler(mHandlerThread.getLooper());
 
-        // setup our unique device name
-        if (TextUtils.isEmpty(SystemProperties.get("net.hostname"))) {
-            String id = Settings.Secure.getString(context.getContentResolver(),
-                    Settings.Secure.ANDROID_ID);
-            if (id != null && id.length() > 0) {
-                String name = new String("android-").concat(id);
-                SystemProperties.set("net.hostname", name);
-            }
-        }
-
         mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
                 Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
 
@@ -866,6 +856,7 @@
 
         mAvoidBadWifiTracker = createAvoidBadWifiTracker(
                 mContext, mHandler, () -> rematchForAvoidBadWifiUpdate());
+        mAvoidBadWifiTracker.start();
     }
 
     private NetworkRequest createInternetRequestForTransport(
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 27e4aa4..a1c3564 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -27,6 +27,7 @@
 import android.content.pm.PackageManager;
 import android.net.INetworkScoreCache;
 import android.net.INetworkScoreService;
+import android.net.NetworkKey;
 import android.net.NetworkScoreManager;
 import android.net.NetworkScorerAppManager;
 import android.net.NetworkScorerAppManager.NetworkScorerAppData;
@@ -472,6 +473,12 @@
     }
 
     @Override
+    public boolean requestScores(NetworkKey[] networks) {
+        // TODO(jjoslin): 12/13/16 - Implement
+        return false;
+    }
+
+    @Override
     protected void dump(final FileDescriptor fd, final PrintWriter writer, final String[] args) {
         mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
         NetworkScorerAppData currentScorer = mNetworkScorerAppManager.getActiveScorer();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f6fea3c..4ea73e7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1220,6 +1220,15 @@
     /**
      * Set while we are wanting to sleep, to prevent any
      * activities from being started/resumed.
+     *
+     * TODO(b/33594039): Clarify the actual state transitions represented by mSleeping.
+     *
+     * Currently mSleeping is set to true when transitioning into the sleep state, and remains true
+     * while in the sleep state until there is a pending transition out of sleep, in which case
+     * mSleeping is set to false, and remains false while awake.
+     *
+     * Whether mSleeping can quickly toggled between true/false without the device actually
+     * display changing states is undefined.
      */
     private boolean mSleeping = false;
 
@@ -1369,6 +1378,7 @@
     boolean mAlwaysFinishActivities = false;
     boolean mForceResizableActivities;
     boolean mSupportsMultiWindow;
+    boolean mSupportsSplitScreenMultiWindow;
     boolean mSupportsFreeformWindowManagement;
     boolean mSupportsPictureInPicture;
     boolean mSupportsLeanbackOnly;
@@ -1551,10 +1561,9 @@
     static final int SYSTEM_USER_UNLOCK_MSG = 59;
     static final int LOG_STACK_STATE = 60;
     static final int VR_MODE_CHANGE_MSG = 61;
-    static final int VR_MODE_APPLY_IF_NEEDED_MSG = 62;
-    static final int SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG = 63;
-    static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 64;
-    static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 65;
+    static final int SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG = 62;
+    static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 63;
+    static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 64;
     static final int START_USER_SWITCH_FG_MSG = 712;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
@@ -2290,14 +2299,6 @@
                 }
                 vrService.setVrMode(vrMode, requestedPackage, userId, callingPackage);
             } break;
-            case VR_MODE_APPLY_IF_NEEDED_MSG: {
-                final ActivityRecord r = (ActivityRecord) msg.obj;
-                final boolean needsVrMode = r != null && r.requestedVrComponent != null;
-                if (needsVrMode) {
-                    applyVrMode(msg.arg1 == 1, r.requestedVrComponent, r.userId,
-                            r.info.getComponentName(), false);
-                }
-            } break;
             case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
                 synchronized (ActivityManagerService.this) {
                     for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
@@ -3070,20 +3071,12 @@
                 mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r));
     }
 
-    void applyVrModeIfNeededLocked(ActivityRecord r, boolean enable) {
-        mHandler.sendMessage(
-                mHandler.obtainMessage(VR_MODE_APPLY_IF_NEEDED_MSG, enable ? 1 : 0, 0, r));
-    }
-
-    private void applyVrMode(boolean enabled, ComponentName packageName, int userId,
-            ComponentName callingPackage, boolean immediate) {
-        VrManagerInternal vrService =
-                LocalServices.getService(VrManagerInternal.class);
-        if (immediate) {
-            vrService.setVrModeImmediate(enabled, packageName, userId, callingPackage);
-        } else {
-            vrService.setVrMode(enabled, packageName, userId, callingPackage);
+    private void notifyVrManagerOfSleepState(boolean isSleeping) {
+        final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
+        if (vrService == null) {
+            return;
         }
+        vrService.onSleepStateChanged(isSleeping);
     }
 
     final void showAskCompatModeDialogLocked(ActivityRecord r) {
@@ -11692,6 +11685,7 @@
             startTimeTrackingFocusedActivityLocked();
             mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
             mStackSupervisor.comeOutOfSleepIfNeededLocked();
+            notifyVrManagerOfSleepState(false);
             updateOomAdjLocked();
         } else if (!mSleeping && shouldSleepLocked()) {
             mSleeping = true;
@@ -11700,6 +11694,7 @@
             }
             mTopProcessState = ActivityManager.PROCESS_STATE_TOP_SLEEPING;
             mStackSupervisor.goingToSleepLocked();
+            notifyVrManagerOfSleepState(true);
             updateOomAdjLocked();
 
             // Initialize the wake times of all processes.
@@ -13147,6 +13142,8 @@
                 mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
 
         final boolean supportsMultiWindow = ActivityManager.supportsMultiWindow();
+        final boolean supportsSplitScreenMultiWindow =
+                ActivityManager.supportsSplitScreenMultiWindow();
         final String debugApp = Settings.Global.getString(resolver, DEBUG_APP);
         final boolean waitForDebugger = Settings.Global.getInt(resolver, WAIT_FOR_DEBUGGER, 0) != 0;
         final boolean alwaysFinishActivities =
@@ -13182,6 +13179,7 @@
                 mSupportsFreeformWindowManagement = false;
                 mSupportsPictureInPicture = false;
             }
+            mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow;
             mWindowManager.setForceResizableTasks(mForceResizableActivities);
             mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
             // This happens before any activities are started, so we can change global configuration
@@ -22233,22 +22231,6 @@
         public SleepToken acquireSleepToken(String tag) {
             Preconditions.checkNotNull(tag);
 
-            ComponentName requestedVrService = null;
-            ComponentName callingVrActivity = null;
-            int userId = -1;
-            synchronized (ActivityManagerService.this) {
-                final ActivityRecord resumedActivity = mStackSupervisor.getResumedActivityLocked();
-                if (resumedActivity != null) {
-                    requestedVrService = resumedActivity.requestedVrComponent;
-                    callingVrActivity = resumedActivity.info.getComponentName();
-                    userId = resumedActivity.userId;
-                }
-            }
-
-            if (requestedVrService != null) {
-                applyVrMode(false, requestedVrService, userId, callingVrActivity, true);
-            }
-
             synchronized (ActivityManagerService.this) {
                 SleepTokenImpl token = new SleepTokenImpl(tag);
                 mSleepTokens.add(token);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 14b843a..814b05a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -233,6 +233,8 @@
                     return runAttachAgent(pw);
                 case "supports-multiwindow":
                     return runSupportsMultiwindow(pw);
+                case "supports-split-screen-multi-window":
+                    return runSupportsSplitScreenMultiwindow(pw);
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -2300,20 +2302,36 @@
     }
 
     int runSupportsMultiwindow(PrintWriter pw) throws RemoteException {
+        final Resources res = getResources(pw);
+        if (res == null) {
+            return -1;
+        }
+        pw.println(res.getBoolean(com.android.internal.R.bool.config_supportsMultiWindow));
+        return 0;
+    }
+
+    int runSupportsSplitScreenMultiwindow(PrintWriter pw) throws RemoteException {
+        final Resources res = getResources(pw);
+        if (res == null) {
+            return -1;
+        }
+        pw.println(
+                res.getBoolean(com.android.internal.R.bool.config_supportsSplitScreenMultiWindow));
+        return 0;
+    }
+
+    private Resources getResources(PrintWriter pw) throws RemoteException {
         // system resources does not contain all the device configuration, construct it manually.
         Configuration config = mInterface.getConfiguration();
         if (config == null) {
             pw.println("Error: Activity manager has no configuration");
-            return -1;
+            return null;
         }
 
         final DisplayMetrics metrics = new DisplayMetrics();
         metrics.setToDefaults();
 
-        Resources res = new Resources(AssetManager.getSystem(), metrics, config);
-
-        pw.println(res.getBoolean(com.android.internal.R.bool.config_supportsMultiWindow));
-        return 0;
+        return new Resources(AssetManager.getSystem(), metrics, config);
     }
 
     @Override
@@ -2495,6 +2513,8 @@
             pw.println("      Rtrieve the configuration and any recent configurations of the device.");
             pw.println("  supports-multiwindow");
             pw.println("      Returns true if the device supports multiwindow.");
+            pw.println("  supports-split-screen-multi-window");
+            pw.println("      Returns true if the device supports split screen multiwindow.");
             pw.println("  suppress-resize-config-changes <true|false>");
             pw.println("      Suppresses configuration changes due to user resizing an activity/task.");
             pw.println("  set-inactive [--user <USER_ID>] <PACKAGE> true|false");
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 436eaf6..671c84e 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2150,9 +2150,7 @@
                     mStackSupervisor.allResumedActivitiesComplete()) {
             // Make sure we have executed any pending transitions, since there
             // should be nothing left to do at this point.
-            mWindowManager.executeAppTransition();
-            mNoAnimActivities.clear();
-            ActivityOptions.abort(options);
+            executeAppTransition(options);
             if (DEBUG_STATES) Slog.d(TAG_STATES,
                     "resumeTopActivityLocked: Top activity resumed " + next);
             if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
@@ -2187,9 +2185,7 @@
                 && mStackSupervisor.allPausedActivitiesComplete()) {
             // Make sure we have executed any pending transitions, since there
             // should be nothing left to do at this point.
-            mWindowManager.executeAppTransition();
-            mNoAnimActivities.clear();
-            ActivityOptions.abort(options);
+            executeAppTransition(options);
             if (DEBUG_STATES) Slog.d(TAG_STATES,
                     "resumeTopActivityLocked: Going to sleep and all paused");
             if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
@@ -2252,9 +2248,7 @@
             // So, nothing else to-do except:
             // Make sure we have executed any pending transitions, since there
             // should be nothing left to do at this point.
-            mWindowManager.executeAppTransition();
-            mNoAnimActivities.clear();
-            ActivityOptions.abort(options);
+            executeAppTransition(options);
             if (DEBUG_STATES) Slog.d(TAG_STATES,
                     "resumeTopActivityLocked: Top activity resumed (dontWaitForPause) " + next);
             if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
@@ -5056,4 +5050,10 @@
             mTaskHistory.get(taskNdx).setLockTaskAuth();
         }
     }
+
+    void executeAppTransition(ActivityOptions options) {
+        mWindowManager.executeAppTransition();
+        mNoAnimActivities.clear();
+        ActivityOptions.abort(options);
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index db9e0f6..fe261f1 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1844,6 +1844,9 @@
         final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
         if (r == null || r.state != RESUMED) {
             mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
+        } else if (r.state == RESUMED) {
+            // Kick off any lingering app transitions form the MoveTaskToFront operation.
+            mFocusedStack.executeAppTransition(targetOptions);
         }
         return false;
     }
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 19bf536..cfe2eb0 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -121,8 +121,6 @@
                 // Some stack visibility might change (e.g. docked stack)
                 mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
                 mWindowManager.executeAppTransition();
-                mService.applyVrModeIfNeededLocked(mStackSupervisor.getResumedActivityLocked(),
-                        true /* enable */);
             } finally {
                 mWindowManager.continueSurfaceLayout();
             }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index a6444bc..9e28068 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1104,7 +1104,7 @@
     }
 
     boolean canGoInDockedStack() {
-        return isResizeable() &&
+        return isResizeable() && mService.mSupportsSplitScreenMultiWindow &&
                 !ActivityInfo.isPreserveOrientationMode(mResizeMode);
     }
 
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 56ba5bb..fb2b961 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -457,6 +457,7 @@
             if (mDownloadXtraDataPending == STATE_PENDING_NETWORK) {
                 xtraDownloadRequest();
             }
+            // Always on, notify HAL so it can get data it needs
             sendMessage(UPDATE_NETWORK_STATE, 0 /*arg*/, network);
         }
     };
@@ -469,6 +470,12 @@
     private final ConnectivityManager.NetworkCallback mSuplConnectivityCallback =
             new ConnectivityManager.NetworkCallback() {
         @Override
+        public void onAvailable(Network network) {
+            // Specific to a change to a SUPL enabled network becoming ready
+            sendMessage(UPDATE_NETWORK_STATE, 0 /*arg*/, network);
+        }
+
+        @Override
         public void onLost(Network network) {
             releaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN);
         }
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 8ca6086..386e78b 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -553,15 +553,21 @@
             @Override
             public NetworkStats getSummaryForAllUid(
                     NetworkTemplate template, long start, long end, boolean includeTags) {
-                @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage);
-                final NetworkStats stats =
-                        getUidComplete().getSummary(template, start, end, accessLevel);
-                if (includeTags) {
-                    final NetworkStats tagStats = getUidTagComplete()
-                            .getSummary(template, start, end, accessLevel);
-                    stats.combineAllValues(tagStats);
+                try {
+                    @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage);
+                    final NetworkStats stats =
+                            getUidComplete().getSummary(template, start, end, accessLevel);
+                    if (includeTags) {
+                        final NetworkStats tagStats = getUidTagComplete()
+                                .getSummary(template, start, end, accessLevel);
+                        stats.combineAllValues(tagStats);
+                    }
+                    return stats;
+                } catch (NullPointerException e) {
+                    // TODO: Track down and fix the cause of this crash and remove this catch block.
+                    Slog.wtf(TAG, "NullPointerException in getSummaryForAllUid", e);
+                    throw e;
                 }
-                return stats;
             }
 
             @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f7cccc8..c65514b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7587,19 +7587,6 @@
                 compilerFilter = getNonProfileGuidedCompilerFilter(compilerFilter);
             }
 
-            // If the OTA updates a system app which was previously preopted to a non-preopted state
-            // the app might end up being verified at runtime. That's because by default the apps
-            // are verify-profile but for preopted apps there's no profile.
-            // Do a hacky check to ensure that if we have no profiles (a reasonable indication
-            // that before the OTA the app was preopted) the app gets compiled with a non-profile
-            // filter (by default interpret-only).
-            // Note that at this stage unused apps are already filtered.
-            if (isSystemApp(pkg) &&
-                    DexFile.isProfileGuidedCompilerFilter(compilerFilter) &&
-                    !Environment.getReferenceProfile(pkg.packageName).exists()) {
-                compilerFilter = getNonProfileGuidedCompilerFilter(compilerFilter);
-            }
-
             // checkProfiles is false to avoid merging profiles during boot which
             // might interfere with background compilation (b/28612421).
             // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index bac7a76..05228ec 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2269,8 +2269,11 @@
 
     private UserInfo createUserInternal(String name, int flags, int parentId,
             String[] disallowedPackages) {
-        if (hasUserRestriction(UserManager.DISALLOW_ADD_USER, UserHandle.getCallingUserId())) {
-            Log.w(LOG_TAG, "Cannot add user. DISALLOW_ADD_USER is enabled.");
+        String restriction = ((flags & UserInfo.FLAG_MANAGED_PROFILE) != 0)
+                ? UserManager.DISALLOW_ADD_MANAGED_PROFILE
+                : UserManager.DISALLOW_ADD_USER;
+        if (hasUserRestriction(restriction, UserHandle.getCallingUserId())) {
+            Log.w(LOG_TAG, "Cannot add user. " + restriction + " is enabled.");
             return null;
         }
         return createUserInternalUnchecked(name, flags, parentId, disallowedPackages);
@@ -2541,9 +2544,16 @@
     public boolean removeUser(int userHandle) {
         Slog.i(LOG_TAG, "removeUser u" + userHandle);
         checkManageOrCreateUsersPermission("Only the system can remove users");
-        if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(
-                UserManager.DISALLOW_REMOVE_USER, false)) {
-            Log.w(LOG_TAG, "Cannot remove user. DISALLOW_REMOVE_USER is enabled.");
+
+        final boolean isManagedProfile;
+        synchronized (mUsersLock) {
+            UserInfo userInfo = getUserInfoLU(userHandle);
+            isManagedProfile = userInfo != null && userInfo.isManagedProfile();
+        }
+        String restriction = isManagedProfile
+                ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE : UserManager.DISALLOW_REMOVE_USER;
+        if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) {
+            Log.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
             return false;
         }
         return removeUserUnchecked(userHandle);
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 7ec3c19..e91cce1 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -75,12 +75,14 @@
             UserManager.DISALLOW_USB_FILE_TRANSFER,
             UserManager.DISALLOW_CONFIG_CREDENTIALS,
             UserManager.DISALLOW_REMOVE_USER,
+            UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
             UserManager.DISALLOW_DEBUGGING_FEATURES,
             UserManager.DISALLOW_CONFIG_VPN,
             UserManager.DISALLOW_CONFIG_TETHERING,
             UserManager.DISALLOW_NETWORK_RESET,
             UserManager.DISALLOW_FACTORY_RESET,
             UserManager.DISALLOW_ADD_USER,
+            UserManager.DISALLOW_ADD_MANAGED_PROFILE,
             UserManager.ENSURE_VERIFY_APPS,
             UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
             UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
@@ -124,6 +126,8 @@
             UserManager.DISALLOW_NETWORK_RESET,
             UserManager.DISALLOW_FACTORY_RESET,
             UserManager.DISALLOW_ADD_USER,
+            UserManager.DISALLOW_ADD_MANAGED_PROFILE,
+            UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
             UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
             UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
             UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
@@ -155,6 +159,13 @@
     );
 
     /**
+     * User restrictions that default to {@code true} for device owners.
+     */
+    private static final Set<String> DEFAULT_ENABLED_FOR_DEVICE_OWNERS = Sets.newArraySet(
+            UserManager.DISALLOW_ADD_MANAGED_PROFILE
+    );
+
+    /**
      * Throws {@link IllegalArgumentException} if the given restriction name is invalid.
      */
     public static boolean isValidRestriction(@NonNull String restriction) {
@@ -249,6 +260,13 @@
     }
 
     /**
+     * Returns the user restrictions that default to {@code true} for device owners.
+     */
+    public static @NonNull Set<String> getDefaultEnabledForDeviceOwner() {
+        return DEFAULT_ENABLED_FOR_DEVICE_OWNERS;
+    }
+
+    /**
      * Takes restrictions that can be set by device owner, and sort them into what should be applied
      * globally and what should be applied only on the current user.
      */
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index afad328..9d4b51f 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -229,6 +229,7 @@
 import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.wm.AppTransition;
+import com.android.server.vr.VrManagerInternal;
 
 import java.io.File;
 import java.io.FileReader;
@@ -6455,6 +6456,7 @@
                 mKeyguardDelegate.onScreenTurnedOff();
             }
         }
+        reportScreenStateToVrManager(false);
     }
 
     // Called on the DisplayManager's DisplayPowerController thread.
@@ -6490,6 +6492,15 @@
                 mKeyguardDelegate.onScreenTurnedOn();
             }
         }
+        reportScreenStateToVrManager(true);
+    }
+
+    private void reportScreenStateToVrManager(boolean isScreenOn) {
+        VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
+        if (vrService == null) {
+            return;
+        }
+        vrService.onScreenStateChanged(isScreenOn);
     }
 
     private void finishWindowsDrawn() {
@@ -6674,9 +6685,7 @@
 
         // Navigation bar and status bar.
         getNonDecorInsetsLw(displayRotation, displayWidth, displayHeight, outInsets);
-        if (mStatusBar != null) {
-            outInsets.top = mStatusBarHeight;
-        }
+        outInsets.top = mStatusBarHeight;
     }
 
     @Override
@@ -6685,7 +6694,7 @@
         outInsets.setEmpty();
 
         // Only navigation bar
-        if (mNavigationBar != null) {
+        if (mHasNavigationBar) {
             int position = navigationBarPosition(displayWidth, displayHeight, displayRotation);
             if (position == NAV_BAR_BOTTOM) {
                 outInsets.bottom = getNavigationBarHeight(displayRotation, mUiMode);
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index ad87a88..0fc1900 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -56,25 +56,27 @@
             int userId, @NonNull ComponentName calling);
 
     /**
-     * Set the current VR mode state immediately.
+     * Set whether the system has acquired a sleep token.
      *
-     * @param enabled {@code true} to enable VR mode.
-     * @param packageName The package name of the requested VrListenerService to bind.
-     * @param userId the user requesting the VrListenerService component.
-     * @param calling the component currently using VR mode, or null to leave unchanged.
+     * @param isAsleep is {@code true} if the device is asleep, or {@code false} otherwise.
      */
-    public abstract void setVrModeImmediate(boolean enabled, @NonNull ComponentName packageName,
-            int userId, @NonNull ComponentName calling);
+    public abstract void onSleepStateChanged(boolean isAsleep);
 
+    /**
+     * Set whether the display used for VR output is on.
+     *
+     * @param isScreenOn is {@code true} if the display is on and can receive commands,
+     *      or {@code false} otherwise.
+     */
+    public abstract void onScreenStateChanged(boolean isScreenOn);
 
-   /**
-    * Return NO_ERROR if the given package is installed on the device and enabled as a
-    * VrListenerService for the given current user, or a negative error code indicating a failure.
-    *
-    * @param packageName the name of the package to check, or null to select the default package.
-    * @return NO_ERROR if the given package is installed and is enabled, or a negative error code
-    *       given in {@link android.service.vr.VrModeException} on failure.
-    */
+    /**
+     * Return NO_ERROR if the given package is installed on the device and enabled as a
+     * VrListenerService for the given current user, or a negative error code indicating a failure.
+     *
+     * @param packageName the name of the package to check, or null to select the default package.
+     * @return NO_ERROR if the given package is installed and is enabled, or a negative error code
+     *       given in {@link android.service.vr.VrModeException} on failure.
+     */
     public abstract int hasVrPackage(@NonNull ComponentName packageName, int userId);
-
 }
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 10a0f6f..1083e0a 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -101,6 +101,14 @@
     private static final int PENDING_STATE_DELAY_MS = 300;
     private static final int EVENT_LOG_SIZE = 32;
     private static final int INVALID_APPOPS_MODE = -1;
+    /** Null set of sleep sleep flags. */
+    private static final int FLAG_NONE = 0;
+    /** Flag set when the device is not sleeping. */
+    private static final int FLAG_AWAKE = 1;
+    /** Flag set when the screen has been turned on. */
+    private static final int FLAG_SCREEN_ON = 2;
+    /** Flag indicating that all system sleep flags have been set.*/
+    private static final int FLAG_ALL = FLAG_AWAKE | FLAG_SCREEN_ON;
 
     private static native void initializeNative();
     private static native void setVrModeNative(boolean enabled);
@@ -110,6 +118,7 @@
     private final IBinder mOverlayToken = new Binder();
 
     // State protected by mLock
+    private boolean mVrModeAllowed;
     private boolean mVrModeEnabled;
     private EnabledComponentsObserver mComponentObserver;
     private ManagedApplicationService mCurrentVrService;
@@ -125,10 +134,64 @@
     private VrState mPendingState;
     private final ArrayDeque<VrState> mLoggingDeque = new ArrayDeque<>(EVENT_LOG_SIZE);
     private final NotificationAccessManager mNotifAccessManager = new NotificationAccessManager();
+    /** Tracks the state of the screen and keyguard UI.*/
+    private int mSystemSleepFlags = FLAG_NONE;
 
     private static final int MSG_VR_STATE_CHANGE = 0;
     private static final int MSG_PENDING_VR_STATE_CHANGE = 1;
 
+    /**
+     * Set whether VR mode may be enabled.
+     * <p/>
+     * If VR mode is not allowed to be enabled, calls to set VR mode will be cached.  When VR mode
+     * is again allowed to be enabled, the most recent cached state will be applied.
+     *
+     * @param allowed {@code true} if calling any of the setVrMode methods may cause the device to
+     *   enter VR mode.
+     */
+    private void setVrModeAllowedLocked(boolean allowed) {
+        if (mVrModeAllowed != allowed) {
+            mVrModeAllowed = allowed;
+            Slog.i(TAG, "VR mode is " + ((allowed) ? "allowed" : "disallowed"));
+            if (mVrModeAllowed) {
+                consumeAndApplyPendingStateLocked();
+            } else {
+                // Set pending state to current state.
+                mPendingState = (mVrModeEnabled && mCurrentVrService != null)
+                    ? new VrState(mVrModeEnabled, mCurrentVrService.getComponent(),
+                        mCurrentVrService.getUserId(), mCurrentVrModeComponent)
+                    : null;
+
+                // Unbind current VR service and do necessary callbacks.
+                updateCurrentVrServiceLocked(false, null, 0, null);
+            }
+        }
+    }
+
+    private void setSleepState(boolean isAsleep) {
+        synchronized(mLock) {
+
+            if (!isAsleep) {
+                mSystemSleepFlags |= FLAG_AWAKE;
+            } else {
+                mSystemSleepFlags &= ~FLAG_AWAKE;
+            }
+
+            setVrModeAllowedLocked(mSystemSleepFlags == FLAG_ALL);
+        }
+    }
+
+    private void setScreenOn(boolean isScreenOn) {
+        synchronized(mLock) {
+            if (isScreenOn) {
+                mSystemSleepFlags |= FLAG_SCREEN_ON;
+            } else {
+                mSystemSleepFlags &= ~FLAG_SCREEN_ON;
+            }
+            setVrModeAllowedLocked(mSystemSleepFlags == FLAG_ALL);
+        }
+    }
+
     private final Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
@@ -148,7 +211,9 @@
                 } break;
                 case MSG_PENDING_VR_STATE_CHANGE : {
                     synchronized(mLock) {
-                        VrManagerService.this.consumeAndApplyPendingStateLocked();
+                        if (mVrModeAllowed) {
+                           VrManagerService.this.consumeAndApplyPendingStateLocked();
+                        }
                     }
                 } break;
                 default :
@@ -268,8 +333,8 @@
             }
             mNotifAccessManager.update(enabledPackages);
 
-            if (mCurrentVrService == null) {
-                return; // No active services
+            if (!mVrModeAllowed) {
+                return; // Don't do anything, we shouldn't be in VR mode.
             }
 
             // If there is a pending state change, we'd better deal with that first
@@ -321,6 +386,7 @@
                 return;
             }
             pw.println("********* Dump of VrManagerService *********");
+            pw.println("VR mode is currently: " + ((mVrModeAllowed) ? "allowed" : "disallowed"));
             pw.println("Previous state transitions:\n");
             String tab = "  ";
             dumpStateTransitions(pw);
@@ -374,13 +440,17 @@
         @Override
         public void setVrMode(boolean enabled, ComponentName packageName, int userId,
                 ComponentName callingPackage) {
-            VrManagerService.this.setVrMode(enabled, packageName, userId, callingPackage, false);
+            VrManagerService.this.setVrMode(enabled, packageName, userId, callingPackage);
         }
 
         @Override
-        public void setVrModeImmediate(boolean enabled, ComponentName packageName, int userId,
-                ComponentName callingPackage) {
-            VrManagerService.this.setVrMode(enabled, packageName, userId, callingPackage, true);
+        public void onSleepStateChanged(boolean isAsleep) {
+            VrManagerService.this.setSleepState(isAsleep);
+        }
+
+        @Override
+        public void onScreenStateChanged(boolean isScreenOn) {
+            VrManagerService.this.setScreenOn(isScreenOn);
         }
 
         @Override
@@ -424,6 +494,10 @@
 
                 mComponentObserver.rebuildAll();
             }
+        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+            synchronized (mLock) {
+                mVrModeAllowed = true;
+            }
         }
     }
 
@@ -466,12 +540,16 @@
                     false, mOverlayToken, null, oldUserId);
         }
 
+        if (!mVrModeEnabled) {
+            return;
+        }
+
         // Apply the restrictions for the current user based on vr state
         String[] exemptions = (exemptedPackage == null) ? new String[0] :
                 new String[] { exemptedPackage };
 
         appOpsManager.setUserRestrictionForUser(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
-                mVrModeEnabled, mOverlayToken, exemptions, newUserId);
+                true, mOverlayToken, exemptions, newUserId);
     }
 
     private void updateDependentAppOpsLocked(String newVrServicePackage, int newUserId,
@@ -512,7 +590,8 @@
 
             boolean validUserComponent = (mComponentObserver.isValid(component, userId) ==
                     EnabledComponentsObserver.NO_ERROR);
-            if (!mVrModeEnabled && !enabled) {
+            boolean goingIntoVrMode = validUserComponent && enabled;
+            if (!mVrModeEnabled && !goingIntoVrMode) {
                 return validUserComponent; // Disabled -> Disabled transition does nothing.
             }
 
@@ -520,29 +599,37 @@
                     ? mCurrentVrService.getComponent().getPackageName() : null;
             final int oldUserId = mCurrentVrModeUser;
 
-            // Always send mode change events.
-            changeVrModeLocked(enabled);
+            // Notify system services and VR HAL of mode change.
+            changeVrModeLocked(goingIntoVrMode);
 
-            if (!enabled || !validUserComponent) {
-                // Unbind whatever is running
+            boolean nothingChanged = false;
+            if (!goingIntoVrMode) {
+                // Not going into VR mode, unbind whatever is running
                 if (mCurrentVrService != null) {
                     Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + " for user " +
                             mCurrentVrService.getUserId());
                     mCurrentVrService.disconnect();
                     mCurrentVrService = null;
+                } else {
+                    nothingChanged = true;
                 }
             } else {
+                // Going into VR mode
                 if (mCurrentVrService != null) {
-                    // Unbind any running service that doesn't match the component/user selection
+                    // Unbind any running service that doesn't match the latest component/user
+                    // selection.
                     if (mCurrentVrService.disconnectIfNotMatching(component, userId)) {
                         Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() +
                                 " for user " + mCurrentVrService.getUserId());
                         createAndConnectService(component, userId);
                         sendUpdatedCaller = true;
+                    } else {
+                        nothingChanged = true;
                     }
-                    // The service with the correct component/user is bound
+                    // The service with the correct component/user is already bound, do nothing.
                 } else {
-                    // Nothing was previously running, bind a new service
+                    // Nothing was previously running, bind a new service for the latest
+                    // component/user selection.
                     createAndConnectService(component, userId);
                     sendUpdatedCaller = true;
                 }
@@ -577,7 +664,10 @@
                     }
                 });
             }
-            logStateLocked();
+
+            if (!nothingChanged) {
+                logStateLocked();
+            }
 
             return validUserComponent;
         } finally {
@@ -784,7 +874,10 @@
                     mPendingState.targetPackageName, mPendingState.userId,
                     mPendingState.callingPackage);
             mPendingState = null;
+        } else {
+            updateCurrentVrServiceLocked(false, null, 0, null);
         }
+
     }
 
     private void logStateLocked() {
@@ -834,13 +927,20 @@
     /*
      * Implementation of VrManagerInternal calls.  These are callable from system services.
      */
-
     private void setVrMode(boolean enabled, @NonNull ComponentName targetPackageName,
-            int userId, @NonNull ComponentName callingPackage, boolean immediate) {
+            int userId, @NonNull ComponentName callingPackage) {
 
         synchronized (mLock) {
+            VrState pending = new VrState(enabled, targetPackageName, userId, callingPackage);
+            if (!mVrModeAllowed) {
+                // We're not allowed to be in VR mode.  Make this state pending.  This will be
+                // applied the next time we are allowed to enter VR mode unless it is superseded by
+                // another call.
+                mPendingState = pending;
+                return;
+            }
 
-            if (!enabled && mCurrentVrService != null && !immediate) {
+            if (!enabled && mCurrentVrService != null) {
                 // If we're transitioning out of VR mode, delay briefly to avoid expensive HAL calls
                 // and service bind/unbind in case we are immediately switching to another VR app.
                 if (mPendingState == null) {
@@ -848,7 +948,7 @@
                             PENDING_STATE_DELAY_MS);
                 }
 
-                mPendingState = new VrState(enabled, targetPackageName, userId, callingPackage);
+                mPendingState = pending;
                 return;
             } else {
                 mHandler.removeMessages(MSG_PENDING_VR_STATE_CHANGE);
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index f8b461e..e6bc7f4 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -390,9 +390,8 @@
                 inputMethodManagerInternal.hideCurrentInputMethod();
                 mImeHideRequested = true;
             }
-        } else if (setMinimizedDockedStack(false)) {
-            mService.mWindowPlacerLocked.performSurfacePlacement();
         }
+        setMinimizedDockedStack(false, false /* animate */);
     }
 
     /**
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index aafc432..4061036 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -153,7 +153,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.ParcelableString;
@@ -4806,6 +4805,20 @@
 
             long ident = mInjector.binderClearCallingIdentity();
             try {
+                final String restriction;
+                if (userHandle == UserHandle.USER_SYSTEM) {
+                    restriction = UserManager.DISALLOW_FACTORY_RESET;
+                } else if (isManagedProfile(userHandle)) {
+                    restriction = UserManager.DISALLOW_REMOVE_MANAGED_PROFILE;
+                } else {
+                    restriction = UserManager.DISALLOW_REMOVE_USER;
+                }
+                if (isAdminAffectedByRestriction(
+                        admin.info.getComponent(), restriction, userHandle)) {
+                    throw new SecurityException("Cannot wipe data. " + restriction
+                            + " restriction is set for user " + userHandle);
+                }
+
                 if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
                     if (!isDeviceOwner(admin.info.getComponent(), userHandle)) {
                         throw new SecurityException(
@@ -4817,34 +4830,21 @@
                         manager.wipe();
                     }
                 }
+
                 boolean wipeExtRequested = (flags & WIPE_EXTERNAL_STORAGE) != 0;
-                // If the admin is the only one who has set the restriction: force wipe, even if
-                // {@link UserManager.DISALLOW_FACTORY_RESET} is set. Reason is that the admin
-                // could remove this user restriction anyway.
-                boolean force = (userHandle == UserHandle.USER_SYSTEM)
-                        && isAdminOnlyOneWhoSetRestriction(admin,
-                                UserManager.DISALLOW_FACTORY_RESET, UserHandle.USER_SYSTEM);
                 wipeDeviceOrUserLocked(wipeExtRequested, userHandle,
-                        "DevicePolicyManager.wipeData() from " + source, force);
+                        "DevicePolicyManager.wipeData() from " + source, /*force=*/ true);
             } finally {
                 mInjector.binderRestoreCallingIdentity(ident);
             }
         }
     }
 
-    private boolean isAdminOnlyOneWhoSetRestriction(ActiveAdmin admin, String userRestriction,
-            int userId) {
-        int source = mUserManager.getUserRestrictionSource(userRestriction, UserHandle.of(userId));
-        if (isDeviceOwner(admin.info.getComponent(), userId)) {
-            return source == UserManager.RESTRICTION_SOURCE_DEVICE_OWNER;
-        } else if (isProfileOwner(admin.info.getComponent(), userId)) {
-            return source == UserManager.RESTRICTION_SOURCE_PROFILE_OWNER;
-        }
-        return false;
-    }
-
-    private void wipeDeviceOrUserLocked(boolean wipeExtRequested, final int userHandle,
-            String reason, boolean force) {
+    private void wipeDeviceOrUserLocked(
+            boolean wipeExtRequested, final int userHandle, String reason, boolean force) {
+        // TODO If split user is enabled and the device owner is set in the primary user (rather
+        // than system), we should probably trigger factory reset. Current code just remove
+        // that user (but still clears FRP...)
         if (userHandle == UserHandle.USER_SYSTEM) {
             wipeDataLocked(wipeExtRequested, reason, force);
         } else {
@@ -4857,10 +4857,12 @@
                             am.switchUser(UserHandle.USER_SYSTEM);
                         }
 
-                        boolean isManagedProfile = isManagedProfile(userHandle);
-                        if (!mUserManager.removeUser(userHandle)) {
+                        boolean userRemoved = force
+                                ? mUserManagerInternal.removeUserEvenWhenDisallowed(userHandle)
+                                : mUserManager.removeUser(userHandle);
+                        if (!userRemoved) {
                             Slog.w(LOG_TAG, "Couldn't remove user " + userHandle);
-                        } else if (isManagedProfile) {
+                        } else if (isManagedProfile(userHandle)) {
                             sendWipeProfileNotification();
                         }
                     } catch (RemoteException re) {
@@ -5948,7 +5950,8 @@
         }
         synchronized (this) {
             enforceCanSetDeviceOwnerLocked(admin, userId);
-            if (getActiveAdminUncheckedLocked(admin, userId) == null
+            final ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(admin, userId);
+            if (activeAdmin == null
                     || getUserData(userId).mRemovingAdmins.contains(admin)) {
                 throw new IllegalArgumentException("Not active admin: " + admin);
             }
@@ -5975,12 +5978,23 @@
             mOwners.writeDeviceOwner();
             updateDeviceOwnerLocked();
             setDeviceOwnerSystemPropertyLocked();
-            Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED);
+
+            // STOPSHIP(b/31952368) Also set this restriction for existing DOs on OTA to Android OC.
+            final Set<String> restrictions =
+                    UserRestrictionsUtils.getDefaultEnabledForDeviceOwner();
+            if (!restrictions.isEmpty()) {
+                for (String restriction : restrictions) {
+                    activeAdmin.ensureUserRestrictions().putBoolean(restriction, true);
+                }
+                saveUserRestrictionsLocked(userId);
+            }
 
             ident = mInjector.binderClearCallingIdentity();
             try {
                 // TODO Send to system too?
-                mContext.sendBroadcastAsUser(intent, new UserHandle(userId));
+                mContext.sendBroadcastAsUser(
+                        new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED),
+                        UserHandle.of(userId));
             } finally {
                 mInjector.binderRestoreCallingIdentity(ident);
             }
@@ -6176,6 +6190,7 @@
             mOwners.setProfileOwner(who, ownerName, userHandle);
             mOwners.writeProfileOwner(userHandle);
             Slog.i(LOG_TAG, "Profile owner set: " + who + " on user " + userHandle);
+
             return true;
         }
     }
@@ -7486,28 +7501,41 @@
     @Override
     public boolean removeUser(ComponentName who, UserHandle userHandle) {
         Preconditions.checkNotNull(who, "ComponentName is null");
-        UserHandle callingUserHandle = mInjector.binderGetCallingUserHandle();
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
         }
+
+        final int callingUserId = mInjector.userHandleGetCallingUserId();
         final long id = mInjector.binderClearCallingIdentity();
         try {
-            int restrictionSource = mUserManager.getUserRestrictionSource(
-                    UserManager.DISALLOW_REMOVE_USER, callingUserHandle);
-            if (restrictionSource != UserManager.RESTRICTION_NOT_SET
-                    && restrictionSource != UserManager.RESTRICTION_SOURCE_DEVICE_OWNER) {
+            String restriction = isManagedProfile(userHandle.getIdentifier())
+                    ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE
+                    : UserManager.DISALLOW_REMOVE_USER;
+            if (isAdminAffectedByRestriction(who, restriction, callingUserId)) {
                 Log.w(LOG_TAG, "The device owner cannot remove a user because "
-                        + "DISALLOW_REMOVE_USER is enabled, and was not set by the device "
-                        + "owner");
+                        + restriction + " is enabled, and was not set by the device owner");
                 return false;
             }
-            return mUserManagerInternal.removeUserEvenWhenDisallowed(
-                    userHandle.getIdentifier());
+            return mUserManagerInternal.removeUserEvenWhenDisallowed(userHandle.getIdentifier());
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
     }
 
+    private boolean isAdminAffectedByRestriction(
+            ComponentName admin, String userRestriction, int userId) {
+        switch(mUserManager.getUserRestrictionSource(userRestriction, UserHandle.of(userId))) {
+            case UserManager.RESTRICTION_NOT_SET:
+                return false;
+            case UserManager.RESTRICTION_SOURCE_DEVICE_OWNER:
+                return !isDeviceOwner(admin, userId);
+            case UserManager.RESTRICTION_SOURCE_PROFILE_OWNER:
+                return !isProfileOwner(admin, userId);
+            default:
+                return true;
+        }
+    }
+
     @Override
     public boolean switchUser(ComponentName who, UserHandle userHandle) {
         Preconditions.checkNotNull(who, "ComponentName is null");
@@ -7613,14 +7641,16 @@
 
             // Save the restriction to ActiveAdmin.
             activeAdmin.ensureUserRestrictions().putBoolean(key, enabledFromThisOwner);
-            saveSettingsLocked(userHandle);
-
-            pushUserRestrictions(userHandle);
-
-            sendChangedNotification(userHandle);
+            saveUserRestrictionsLocked(userHandle);
         }
     }
 
+    private void saveUserRestrictionsLocked(int userId) {
+        saveSettingsLocked(userId);
+        pushUserRestrictions(userId);
+        sendChangedNotification(userId);
+    }
+
     private void pushUserRestrictions(int userId) {
         synchronized (this) {
             final Bundle global;
@@ -8862,26 +8892,33 @@
         if (!hasFeatureManagedUsers()) {
             return CODE_MANAGED_USERS_NOT_SUPPORTED;
         }
-        synchronized (this) {
-            if (mOwners.hasDeviceOwner()) {
-                // STOPSHIP Only allow creating a managed profile if allowed by the device
-                // owner. http://b/31952368
-                if (mInjector.userManagerIsSplitSystemUser()) {
-                    if (callingUserId == UserHandle.USER_SYSTEM) {
-                        // Managed-profiles cannot be setup on the system user.
-                        return CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER;
-                    }
-                }
-            }
+        if (callingUserId == UserHandle.USER_SYSTEM
+                && mInjector.userManagerIsSplitSystemUser()) {
+            // Managed-profiles cannot be setup on the system user.
+            return CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER;
         }
         if (getProfileOwner(callingUserId) != null) {
             // Managed user cannot have a managed profile.
             return CODE_USER_HAS_PROFILE_OWNER;
         }
-        boolean canRemoveProfile =
-                !mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_USER);
         final long ident = mInjector.binderClearCallingIdentity();
         try {
+             /* STOPSHIP(b/31952368) Reinstate a check similar to this once ManagedProvisioning
+                   uses checkProvisioningPreCondition (see ag/1607846) and passes the packageName
+                   there. In isProvisioningAllowed we should check isCallerDeviceOwner, but for
+                   managed provisioning we need to check the package that is going to be set as PO
+                if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE)) {
+                    if (!isCallerDeviceOwner(callingUid)
+                            || isAdminAffectedByRestriction(mOwners.getDeviceOwnerComponent(),
+                                    UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserId)) {
+                    // Caller is not DO or the restriction was set by the system.
+                    return false;
+                    }
+                } */
+            // TODO: Allow it if the caller is the DO? DO could just call removeUser() before
+            // provisioning, so not strictly required...
+            boolean canRemoveProfile = !mUserManager.hasUserRestriction(
+                        UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, UserHandle.of(callingUserId));
             if (!mUserManager.canAddMoreManagedProfiles(callingUserId, canRemoveProfile)) {
                 return CODE_CANNOT_ADD_MANAGED_PROFILE;
             }
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index ef4bc02..96c8e9f 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -7,6 +7,7 @@
 import android.os.Build;
 import android.os.SystemProperties;
 import android.system.OsConstants;
+import android.text.TextUtils;
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.UnsupportedEncodingException;
@@ -629,7 +630,8 @@
     protected void addCommonClientTlvs(ByteBuffer buf) {
         addTlv(buf, DHCP_MAX_MESSAGE_SIZE, (short) MAX_LENGTH);
         addTlv(buf, DHCP_VENDOR_CLASS_ID, getVendorId());
-        addTlv(buf, DHCP_HOST_NAME, getHostname());
+        final String hn = getHostname();
+        if (!TextUtils.isEmpty(hn)) addTlv(buf, DHCP_HOST_NAME, hn);
     }
 
     /**
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 39f14e5..58b2dec 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -438,6 +438,9 @@
         mCallback = new LoggingCallbackWrapper(callback);
         mNwService = nwService;
 
+        mLocalLog = new LocalLog(MAX_LOG_RECORDS);
+        mMsgStateLogger = new MessageHandlingLogger();
+
         mNetlinkTracker = new NetlinkTracker(
                 mInterfaceName,
                 new NetlinkTracker.Callback() {
@@ -451,48 +454,79 @@
                 super.interfaceAdded(iface);
                 if (mClatInterfaceName.equals(iface)) {
                     mCallback.setNeighborDiscoveryOffload(false);
+                } else if (!mInterfaceName.equals(iface)) {
+                    return;
                 }
+
+                final String msg = "interfaceAdded(" + iface +")";
+                logMsg(msg);
             }
 
             @Override
             public void interfaceRemoved(String iface) {
                 super.interfaceRemoved(iface);
+                // TODO: Also observe mInterfaceName going down and take some
+                // kind of appropriate action.
                 if (mClatInterfaceName.equals(iface)) {
                     // TODO: consider sending a message to the IpManager main
                     // StateMachine thread, in case "NDO enabled" state becomes
                     // tied to more things that 464xlat operation.
                     mCallback.setNeighborDiscoveryOffload(true);
+                } else if (!mInterfaceName.equals(iface)) {
+                    return;
                 }
+
+                final String msg = "interfaceRemoved(" + iface +")";
+                logMsg(msg);
+            }
+
+            private void logMsg(String msg) {
+                Log.d(mTag, msg);
+                getHandler().post(() -> { mLocalLog.log("OBSERVED " + msg); });
             }
         };
 
-        try {
-            mNwService.registerObserver(mNetlinkTracker);
-        } catch (RemoteException e) {
-            Log.e(mTag, "Couldn't register NetlinkTracker: " + e.toString());
-        }
+        mLinkProperties = new LinkProperties();
+        mLinkProperties.setInterfaceName(mInterfaceName);
 
-        mAvoidBadWifiTracker = new AvoidBadWifiTracker(mContext, getHandler());
-
-        resetLinkProperties();
+        mAvoidBadWifiTracker = new AvoidBadWifiTracker(mContext, getHandler(),
+                () -> { mLocalLog.log("OBSERVED AvoidBadWifi changed"); });
 
         mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
                 mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
         mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
                 mTag + ".EVENT_DHCPACTION_TIMEOUT", EVENT_DHCPACTION_TIMEOUT);
 
-        // Super simple StateMachine.
+        // Anything the StateMachine may access must have been instantiated
+        // before this point.
+        configureAndStartStateMachine();
+
+        // Anything that may send messages to the StateMachine must only be
+        // configured to do so after the StateMachine has started (above).
+        startStateMachineUpdaters();
+    }
+
+    private void configureAndStartStateMachine() {
         addState(mStoppedState);
         addState(mStartedState);
             addState(mRunningState, mStartedState);
         addState(mStoppingState);
 
         setInitialState(mStoppedState);
-        mLocalLog = new LocalLog(MAX_LOG_RECORDS);
-        mMsgStateLogger = new MessageHandlingLogger();
+
         super.start();
     }
 
+    private void startStateMachineUpdaters() {
+        try {
+            mNwService.registerObserver(mNetlinkTracker);
+        } catch (RemoteException e) {
+            Log.e(mTag, "Couldn't register NetlinkTracker: " + e.toString());
+        }
+
+        mAvoidBadWifiTracker.start();
+    }
+
     @Override
     protected void onQuitting() {
         mCallback.onQuit();
@@ -501,6 +535,7 @@
     // Shut down this IpManager instance altogether.
     public void shutdown() {
         stop();
+        mAvoidBadWifiTracker.shutdown();
         quit();
     }
 
diff --git a/services/net/java/android/net/util/AvoidBadWifiTracker.java b/services/net/java/android/net/util/AvoidBadWifiTracker.java
index c14e811..2abaeb1 100644
--- a/services/net/java/android/net/util/AvoidBadWifiTracker.java
+++ b/services/net/java/android/net/util/AvoidBadWifiTracker.java
@@ -57,7 +57,11 @@
     private final Context mContext;
     private final Handler mHandler;
     private final Runnable mReevaluateRunnable;
+    private final Uri mUri;
+    private final ContentResolver mResolver;
     private final SettingObserver mSettingObserver;
+    private final BroadcastReceiver mBroadcastReceiver;
+
     private volatile boolean mAvoidBadWifi = true;
 
     public AvoidBadWifiTracker(Context ctx, Handler handler) {
@@ -68,19 +72,36 @@
         mContext = ctx;
         mHandler = handler;
         mReevaluateRunnable = () -> { if (update() && cb != null) cb.run(); };
+        mUri = Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI);
+        mResolver = mContext.getContentResolver();
         mSettingObserver = new SettingObserver();
-
-        final IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
-        mContext.registerReceiverAsUser(new BroadcastReceiver() {
+        mBroadcastReceiver = new BroadcastReceiver() {
+            @Override
             public void onReceive(Context context, Intent intent) {
                 reevaluate();
             }
-        }, UserHandle.ALL, intentFilter, null, null);
+        };
 
         update();
     }
 
+    public void start() {
+        mResolver.registerContentObserver(mUri, false, mSettingObserver);
+
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+        mContext.registerReceiverAsUser(
+                mBroadcastReceiver, UserHandle.ALL, intentFilter, null, null);
+
+        reevaluate();
+    }
+
+    public void shutdown() {
+        mResolver.unregisterContentObserver(mSettingObserver);
+
+        mContext.unregisterReceiver(mBroadcastReceiver);
+    }
+
     public boolean currentValue() {
         return mAvoidBadWifi;
     }
@@ -100,8 +121,7 @@
     }
 
     public String getSettingsValue() {
-        final ContentResolver resolver = mContext.getContentResolver();
-        return Settings.Global.getString(resolver, NETWORK_AVOID_BAD_WIFI);
+        return Settings.Global.getString(mResolver, NETWORK_AVOID_BAD_WIFI);
     }
 
     @VisibleForTesting
@@ -117,12 +137,8 @@
     }
 
     private class SettingObserver extends ContentObserver {
-        private final Uri mUri = Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI);
-
         public SettingObserver() {
             super(null);
-            final ContentResolver resolver = mContext.getContentResolver();
-            resolver.registerContentObserver(mUri, false, this);
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 0c00886..5b6d31b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -49,6 +49,7 @@
 import com.android.internal.R;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.pm.UserRestrictionsUtils;
 
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
@@ -1190,6 +1191,22 @@
         assertTrue(dpm.setDeviceOwner(admin1, "owner-name",
                 UserHandle.USER_SYSTEM));
 
+        // Check that the user restrictions that are enabled by default are set. Then unset them.
+        String[] defaultRestrictions = UserRestrictionsUtils
+                        .getDefaultEnabledForDeviceOwner().toArray(new String[0]);
+        DpmTestUtils.assertRestrictions(
+                DpmTestUtils.newRestrictions(defaultRestrictions),
+                dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+        );
+        DpmTestUtils.assertRestrictions(
+                DpmTestUtils.newRestrictions(defaultRestrictions),
+                dpm.getUserRestrictions(admin1)
+        );
+
+        for (String restriction : defaultRestrictions) {
+            dpm.clearUserRestriction(admin1, restriction);
+        }
+
         DpmTestUtils.assertRestrictions(
                 DpmTestUtils.newRestrictions(),
                 dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
@@ -2188,7 +2205,7 @@
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 DevicePolicyManager.CODE_OK);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
-                DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+                DevicePolicyManager.CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER);
         assertCheckProvisioningPreCondition(
                 DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
                 DevicePolicyManager.CODE_OK);
@@ -2226,7 +2243,7 @@
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 DevicePolicyManager.CODE_OK);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
-                DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+                DevicePolicyManager.CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER);
         assertCheckProvisioningPreCondition(
                 DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
                 DevicePolicyManager.CODE_OK);
@@ -2368,7 +2385,9 @@
         when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
                 .thenReturn(true);
         when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
-        when(mContext.userManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_USER))
+        when(mContext.userManager.hasUserRestriction(
+                UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+                UserHandle.of(DpmMockContext.CALLER_USER_HANDLE)))
                 .thenReturn(true);
         when(mContext.userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
                 false /* we can't remove a managed profile */)).thenReturn(false);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 40d8ac0..9b2c94e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -241,15 +241,14 @@
         }
     }
 
-    // Make sure createProfile would fail if we have DISALLOW_ADD_USER.
+    // Make sure createUser would fail if we have DISALLOW_ADD_USER.
     @MediumTest
-    public void testCreateProfileForUser_disallowAddUser() throws Exception {
+    public void testCreateUser_disallowAddUser() throws Exception {
         final int primaryUserId = mUserManager.getPrimaryUser().id;
         final UserHandle primaryUserHandle = new UserHandle(primaryUserId);
         mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, true, primaryUserHandle);
         try {
-            UserInfo userInfo = createProfileForUser("Managed",
-                    UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+            UserInfo userInfo = createUser("SecondaryUser", /*flags=*/ 0);
             assertNull(userInfo);
         } finally {
             mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, false,
@@ -257,17 +256,51 @@
         }
     }
 
-    // Make sure createProfileEvenWhenDisallowedForUser bypass DISALLOW_ADD_USER.
+    // Make sure createProfile would fail if we have DISALLOW_ADD_MANAGED_PROFILE.
+    @MediumTest
+    public void testCreateProfileForUser_disallowAddManagedProfile() throws Exception {
+        final int primaryUserId = mUserManager.getPrimaryUser().id;
+        final UserHandle primaryUserHandle = new UserHandle(primaryUserId);
+        mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
+                primaryUserHandle);
+        try {
+            UserInfo userInfo = createProfileForUser("Managed",
+                    UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+            assertNull(userInfo);
+        } finally {
+            mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, false,
+                    primaryUserHandle);
+        }
+    }
+
+    // Make sure createProfileEvenWhenDisallowedForUser bypass DISALLOW_ADD_MANAGED_PROFILE.
     @MediumTest
     public void testCreateProfileForUserEvenWhenDisallowed() throws Exception {
         final int primaryUserId = mUserManager.getPrimaryUser().id;
         final UserHandle primaryUserHandle = new UserHandle(primaryUserId);
-        mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, true, primaryUserHandle);
+        mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
+                primaryUserHandle);
         try {
             UserInfo userInfo = createProfileEvenWhenDisallowedForUser("Managed",
                     UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
             assertNotNull(userInfo);
         } finally {
+            mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, false,
+                    primaryUserHandle);
+        }
+    }
+
+    // createProfile succeeds even if DISALLOW_ADD_USER is set
+    @MediumTest
+    public void testCreateProfileForUser_disallowAddUser() throws Exception {
+        final int primaryUserId = mUserManager.getPrimaryUser().id;
+        final UserHandle primaryUserHandle = new UserHandle(primaryUserId);
+        mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, true, primaryUserHandle);
+        try {
+            UserInfo userInfo = createProfileForUser("Managed",
+                    UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+            assertNotNull(userInfo);
+        } finally {
             mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, false,
                     primaryUserHandle);
         }
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index f541f70..cb32d1f 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -121,6 +121,23 @@
         endTime = timeStamp;
     }
 
+    void updateChooserCounts(String packageName, String category, String action) {
+        UsageStats usageStats = getOrCreateUsageStats(packageName);
+        if (usageStats.mChooserCounts == null) {
+            usageStats.mChooserCounts = new ArrayMap<>();
+        }
+        ArrayMap<String, Integer> chooserCounts;
+        final int idx = usageStats.mChooserCounts.indexOfKey(action);
+        if (idx < 0) {
+            chooserCounts = new ArrayMap<>();
+            usageStats.mChooserCounts.put(action, chooserCounts);
+        } else {
+            chooserCounts = usageStats.mChooserCounts.valueAt(idx);
+        }
+        int currentCount = chooserCounts.getOrDefault(category, 0);
+        chooserCounts.put(category, currentCount + 1);
+    }
+
     void updateConfigurationStats(Configuration config, long timeStamp) {
         if (activeConfiguration != null) {
             ConfigurationStats activeStats = configurations.get(activeConfiguration);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 849262e..1b28db7 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -17,6 +17,7 @@
 package com.android.server.usage;
 
 import android.app.usage.TimeSparseArray;
+import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManager;
 import android.os.Build;
 import android.util.AtomicFile;
@@ -502,6 +503,12 @@
             pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_DAILY],
                     mCal.getTimeInMillis());
 
+            mCal.setTimeInMillis(currentTimeMillis);
+            mCal.addDays(-14);
+            for (int i = 0; i < mIntervalDirs.length; ++i) {
+                pruneChooserCountsOlderThan(mIntervalDirs[i], mCal.getTimeInMillis());
+            }
+
             // We must re-index our file list or we will be trying to read
             // deleted files.
             indexFilesLocked();
@@ -531,6 +538,43 @@
         }
     }
 
+    private static void pruneChooserCountsOlderThan(File dir, long expiryTime) {
+        File[] files = dir.listFiles();
+        if (files != null) {
+            for (File f : files) {
+                String path = f.getPath();
+                if (path.endsWith(BAK_SUFFIX)) {
+                    f = new File(path.substring(0, path.length() - BAK_SUFFIX.length()));
+                }
+
+                long beginTime;
+                try {
+                    beginTime = UsageStatsXml.parseBeginTime(f);
+                } catch (IOException e) {
+                    beginTime = 0;
+                }
+
+                if (beginTime < expiryTime) {
+                    try {
+                        final AtomicFile af = new AtomicFile(f);
+                        final IntervalStats stats = new IntervalStats();
+                        UsageStatsXml.read(af, stats);
+                        final int pkgCount = stats.packageStats.size();
+                        for (int i = 0; i < pkgCount; i++) {
+                            UsageStats pkgStats = stats.packageStats.valueAt(i);
+                            if (pkgStats.mChooserCounts != null) {
+                                pkgStats.mChooserCounts.clear();
+                            }
+                        }
+                        UsageStatsXml.write(af, stats);
+                    } catch (IOException e) {
+                        Slog.e(TAG, "Failed to delete chooser counts from usage stats file", e);
+                    }
+                }
+            }
+        }
+    }
+
     /**
      * Update the stats in the database. They may not be written to disk immediately.
      */
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 515370f..4bfc3df 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1379,6 +1379,31 @@
             }
             UsageStatsService.this.dump(args, pw);
         }
+
+        @Override
+        public void reportChooserSelection(String packageName, int userId, String contentType,
+                                           String[] annotations, String action) {
+            if (packageName == null) {
+                Slog.w(TAG, "Event report user selecting a null package");
+                return;
+            }
+
+            UsageEvents.Event event = new UsageEvents.Event();
+            event.mPackage = packageName;
+
+            // This will later be converted to system time.
+            event.mTimeStamp = SystemClock.elapsedRealtime();
+
+            event.mEventType = Event.CHOOSER_ACTION;
+
+            event.mAction = action;
+
+            event.mContentType = contentType;
+
+            event.mContentAnnotations = annotations;
+
+            mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
+        }
     }
 
     /**
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index 03cee9c..96f3305 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -26,6 +26,7 @@
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
 import android.content.res.Configuration;
+import android.util.ArrayMap;
 
 import java.io.IOException;
 import java.net.ProtocolException;
@@ -37,6 +38,11 @@
     private static final String PACKAGES_TAG = "packages";
     private static final String PACKAGE_TAG = "package";
 
+    private static final String CHOOSER_COUNT_TAG = "chosen_action";
+    private static final String CATEGORY_TAG = "category";
+    private static final String NAME = "name";
+    private static final String COUNT = "count";
+
     private static final String CONFIGURATIONS_TAG = "configurations";
     private static final String CONFIG_TAG = "config";
 
@@ -59,7 +65,7 @@
     private static final String TIME_ATTR = "time";
 
     private static void loadUsageStats(XmlPullParser parser, IntervalStats statsOut)
-            throws IOException {
+            throws XmlPullParserException, IOException {
         final String pkg = parser.getAttributeValue(null, PACKAGE_ATTR);
         if (pkg == null) {
             throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present");
@@ -72,6 +78,51 @@
                 parser, LAST_TIME_ACTIVE_ATTR);
         stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
         stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
+        int eventCode;
+        while ((eventCode = parser.next()) != XmlPullParser.END_DOCUMENT) {
+            final String tag = parser.getName();
+            if (eventCode == XmlPullParser.END_TAG && tag.equals(PACKAGE_TAG)) {
+                break;
+            }
+            if (eventCode != XmlPullParser.START_TAG) {
+                continue;
+            }
+            if (tag.equals(CHOOSER_COUNT_TAG)) {
+                String action = XmlUtils.readStringAttribute(parser, NAME);
+                loadChooserCounts(parser, stats, action);
+            }
+        }
+    }
+
+    private static void loadChooserCounts(
+            XmlPullParser parser, UsageStats usageStats, String action)
+            throws XmlPullParserException, IOException {
+        if (action == null) {
+            return;
+        }
+        if (usageStats.mChooserCounts == null) {
+            usageStats.mChooserCounts = new ArrayMap<>();
+        }
+        if (!usageStats.mChooserCounts.containsKey(action)) {
+            ArrayMap<String, Integer> counts = new ArrayMap<>();
+            usageStats.mChooserCounts.put(action, counts);
+        }
+
+        int eventCode;
+        while ((eventCode = parser.next()) != XmlPullParser.END_DOCUMENT) {
+            final String tag = parser.getName();
+            if (eventCode == XmlPullParser.END_TAG && tag.equals(CHOOSER_COUNT_TAG)) {
+                break;
+            }
+            if (eventCode != XmlPullParser.START_TAG) {
+                continue;
+            }
+            if (tag.equals(CATEGORY_TAG)) {
+                String category = XmlUtils.readStringAttribute(parser, NAME);
+                int count = XmlUtils.readIntAttribute(parser, COUNT);
+                usageStats.mChooserCounts.get(action).put(category, count);
+            }
+        }
     }
 
     private static void loadConfigStats(XmlPullParser parser, IntervalStats statsOut)
@@ -135,10 +186,45 @@
         XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName);
         XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground);
         XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent);
-
+        writeChooserCounts(xml, usageStats);
         xml.endTag(null, PACKAGE_TAG);
     }
 
+    private static void writeChooserCounts(XmlSerializer xml, final UsageStats usageStats)
+            throws IOException {
+        if (usageStats == null || usageStats.mChooserCounts == null ||
+                usageStats.mChooserCounts.keySet().isEmpty()) {
+            return;
+        }
+        final int chooserCountSize = usageStats.mChooserCounts.size();
+        for (int i = 0; i < chooserCountSize; i++) {
+            final String action = usageStats.mChooserCounts.keyAt(i);
+            final ArrayMap<String, Integer> counts = usageStats.mChooserCounts.valueAt(i);
+            if (action == null || counts == null || counts.isEmpty()) {
+                continue;
+            }
+            xml.startTag(null, CHOOSER_COUNT_TAG);
+            XmlUtils.writeStringAttribute(xml, NAME, action);
+            writeCountsForAction(xml, counts);
+            xml.endTag(null, CHOOSER_COUNT_TAG);
+        }
+    }
+
+    private static void writeCountsForAction(XmlSerializer xml, ArrayMap<String, Integer> counts)
+            throws IOException {
+        final int countsSize = counts.size();
+        for (int i = 0; i < countsSize; i++) {
+            String key = counts.keyAt(i);
+            int count = counts.valueAt(i);
+            if (count > 0) {
+                xml.startTag(null, CATEGORY_TAG);
+                XmlUtils.writeStringAttribute(xml, NAME, key);
+                XmlUtils.writeIntAttribute(xml, COUNT, count);
+                xml.endTag(null, CATEGORY_TAG);
+            }
+        }
+    }
+
     private static void writeConfigStats(XmlSerializer xml, final IntervalStats stats,
             final ConfigurationStats configStats, boolean isActive) throws IOException {
         xml.startTag(null, CONFIG_TAG);
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 59e4c80..ba770ef 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -183,6 +183,15 @@
         for (IntervalStats stats : mCurrentStats) {
             if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
                 stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
+            } else if (event.mEventType == UsageEvents.Event.CHOOSER_ACTION) {
+                stats.updateChooserCounts(event.mPackage, event.mContentType, event.mAction);
+                String[] annotations = event.mContentAnnotations;
+                if (annotations != null) {
+                    for (String annotation : annotations) {
+                        // TODO(kanlig): update with confidences of annotations.
+                        stats.updateChooserCounts(event.mPackage, annotation, event.mAction);
+                    }
+                }
             } else {
                 stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
             }
@@ -520,6 +529,32 @@
         }
         pw.decreaseIndent();
 
+        pw.println();
+        pw.increaseIndent();
+        pw.println("ChooserCounts");
+        pw.increaseIndent();
+        for (UsageStats usageStats : pkgStats.values()) {
+            pw.printPair("package", usageStats.mPackageName);
+            if (usageStats.mChooserCounts != null) {
+                final int chooserCountSize = usageStats.mChooserCounts.size();
+                for (int i = 0; i < chooserCountSize; i++) {
+                    final String action = usageStats.mChooserCounts.keyAt(i);
+                    final ArrayMap<String, Integer> counts = usageStats.mChooserCounts.valueAt(i);
+                    final int annotationSize = counts.size();
+                    for (int j = 0; j < annotationSize; j++) {
+                        final String key = counts.keyAt(j);
+                        final int count = counts.valueAt(j);
+                        if (count != 0) {
+                            pw.printPair("ChooserCounts", action + ":" + key + " is " +
+                                    Integer.toString(count));
+                            pw.println();
+                        }
+                    }
+                }
+            }
+            pw.println();
+        }
+
         pw.println("configurations");
         pw.increaseIndent();
         final ArrayMap<Configuration, ConfigurationStats> configStats = stats.configurations;
@@ -593,6 +628,8 @@
                 return "USER_INTERACTION";
             case UsageEvents.Event.SHORTCUT_INVOCATION:
                 return "SHORTCUT_INVOCATION";
+            case UsageEvents.Event.CHOOSER_ACTION:
+                return "CHOOSER_ACTION";
             default:
                 return "UNKNOWN";
         }
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 581dbe3..db7a31a 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -606,8 +606,12 @@
                 } else {
                     Slog.e(TAG, "nativeGetAccessoryStrings failed");
                 }
-            } else if (!mConnected && !enteringAccessoryMode) {
-                notifyAccessoryModeExit();
+            } else {
+                if (!enteringAccessoryMode) {
+                    notifyAccessoryModeExit();
+                } else if (DEBUG) {
+                    Slog.v(TAG, "Debouncing accessory mode exit");
+                }
             }
         }
 
@@ -813,6 +817,9 @@
                     break;
                 }
                 case MSG_ACCESSORY_MODE_ENTER_TIMEOUT: {
+                    if (DEBUG) {
+                        Slog.v(TAG, "Accessory mode enter timeout: " + mConnected);
+                    }
                     if (!mConnected) {
                         notifyAccessoryModeExit();
                     }
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
index a317994..0e9a485 100644
--- a/test-runner/Android.mk
+++ b/test-runner/Android.mk
@@ -21,7 +21,6 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_JAVA_LIBRARIES := core-oj core-libart junit framework
-LOCAL_STATIC_JAVA_LIBRARIES := junit-runner
 
 LOCAL_MODULE:= android.test.runner
 
diff --git a/test-runner/src/junit/MODULE_LICENSE_CPL b/test-runner/src/junit/MODULE_LICENSE_CPL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test-runner/src/junit/MODULE_LICENSE_CPL
diff --git a/test-runner/src/junit/README.android b/test-runner/src/junit/README.android
new file mode 100644
index 0000000..1384a1f
--- /dev/null
+++ b/test-runner/src/junit/README.android
@@ -0,0 +1,11 @@
+URL: https://github.com/junit-team/junit4
+License: Common Public License Version 1.0
+License File: cpl-v10.html
+
+This is JUnit 4.10 source that was previously part of the Android Public API.
+Where necessary it has been patched to be compatible (according to Android API
+requirements) with JUnit 3.8.
+
+These are copied here to ensure that the android.test.runner target remains
+compatible with the last version of the Android API (25) that contained these
+classes even when external/junit is upgraded to a later version.
diff --git a/test-runner/src/junit/cpl-v10.html b/test-runner/src/junit/cpl-v10.html
new file mode 100644
index 0000000..36aa208
--- /dev/null
+++ b/test-runner/src/junit/cpl-v10.html
@@ -0,0 +1,125 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<HTML>
+<HEAD>
+<TITLE>Common Public License - v 1.0</TITLE>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</HEAD>
+
+<BODY BGCOLOR="#FFFFFF" VLINK="#800000">
+
+
+<P ALIGN="CENTER"><B>Common Public License - v 1.0</B>
+<P><B></B><FONT SIZE="3"></FONT>
+<P><FONT SIZE="3"></FONT><FONT SIZE="2">THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT").  ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.</FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"><B>1.  DEFINITIONS</B></FONT>
+<P><FONT SIZE="2">"Contribution" means:</FONT>
+
+<UL><FONT SIZE="2">a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and<BR CLEAR="LEFT">
+b) in the case of each subsequent Contributor:</FONT></UL>
+
+
+<UL><FONT SIZE="2">i)	 	changes to the Program, and</FONT></UL>
+
+
+<UL><FONT SIZE="2">ii)		additions to the Program;</FONT></UL>
+
+
+<UL><FONT SIZE="2">where such changes and/or additions to the Program originate from and are distributed by that particular Contributor.  </FONT><FONT SIZE="2">A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf.  </FONT><FONT SIZE="2">Contributions do not include additions to the Program which:  (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.  </FONT></UL>
+
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">"Contributor" means any person or entity that distributes the Program.</FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.  </FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2">"Program" means the Contributions distributed in accordance with this Agreement.</FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.</FONT>
+<P><FONT SIZE="2"><B></B></FONT>
+<P><FONT SIZE="2"><B>2.  GRANT OF RIGHTS</B></FONT>
+
+<UL><FONT SIZE="2"></FONT><FONT SIZE="2">a)	</FONT><FONT SIZE="2">Subject to the terms of this Agreement, each Contributor hereby grants</FONT><FONT SIZE="2"> Recipient a non-exclusive, worldwide, royalty-free copyright license to</FONT><FONT SIZE="2" COLOR="#FF0000"> </FONT><FONT SIZE="2">reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.</FONT></UL>
+
+
+<UL><FONT SIZE="2"></FONT></UL>
+
+
+<UL><FONT SIZE="2"></FONT><FONT SIZE="2">b) 	Subject to the terms of this Agreement, each Contributor hereby grants </FONT><FONT SIZE="2">Recipient a non-exclusive, worldwide,</FONT><FONT SIZE="2" COLOR="#008000"> </FONT><FONT SIZE="2">royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form.  This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents.  The patent license shall not apply to any other combinations which include the Contribution.  No hardware per se is licensed hereunder.   </FONT></UL>
+
+
+<UL><FONT SIZE="2"></FONT></UL>
+
+
+<UL><FONT SIZE="2">c)	Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity.  Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise.  As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any.  For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.</FONT></UL>
+
+
+<UL><FONT SIZE="2"></FONT></UL>
+
+
+<UL><FONT SIZE="2">d)	Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. </FONT></UL>
+
+
+<UL><FONT SIZE="2"></FONT></UL>
+
+<P><FONT SIZE="2"><B>3.  REQUIREMENTS</B></FONT>
+<P><FONT SIZE="2"><B></B>A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:</FONT>
+
+<UL><FONT SIZE="2">a)	it complies with the terms and conditions of this Agreement; and</FONT></UL>
+
+
+<UL><FONT SIZE="2">b)	its license agreement:</FONT></UL>
+
+
+<UL><FONT SIZE="2">i)	effectively disclaims</FONT><FONT SIZE="2"> on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; </FONT></UL>
+
+
+<UL><FONT SIZE="2">ii) 	effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; </FONT></UL>
+
+
+<UL><FONT SIZE="2">iii)</FONT><FONT SIZE="2">	states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and</FONT></UL>
+
+
+<UL><FONT SIZE="2">iv)	states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.</FONT><FONT SIZE="2" COLOR="#0000FF"> </FONT><FONT SIZE="2" COLOR="#FF0000"></FONT></UL>
+
+
+<UL><FONT SIZE="2" COLOR="#FF0000"></FONT><FONT SIZE="2"></FONT></UL>
+
+<P><FONT SIZE="2">When the Program is made available in source code form:</FONT>
+
+<UL><FONT SIZE="2">a)	it must be made available under this Agreement; and </FONT></UL>
+
+
+<UL><FONT SIZE="2">b)	a copy of this Agreement must be included with each copy of the Program.  </FONT></UL>
+
+<P><FONT SIZE="2"></FONT><FONT SIZE="2" COLOR="#0000FF"><STRIKE></STRIKE></FONT>
+<P><FONT SIZE="2" COLOR="#0000FF"><STRIKE></STRIKE></FONT><FONT SIZE="2">Contributors may not remove or alter any copyright notices contained within the Program.  </FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.  </FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"><B>4.  COMMERCIAL DISTRIBUTION</B></FONT>
+<P><FONT SIZE="2">Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like.  While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors.   Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering.  The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement.  In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations.  The Indemnified Contributor may participate in any such claim at its own expense.</FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">For example, a Contributor might include the Program in a commercial product offering, Product X.  That Contributor is then a Commercial Contributor.  If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone.  Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.</FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2" COLOR="#0000FF"></FONT>
+<P><FONT SIZE="2" COLOR="#0000FF"></FONT><FONT SIZE="2"><B>5.  NO WARRANTY</B></FONT>
+<P><FONT SIZE="2">EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is</FONT><FONT SIZE="2"> solely responsible for determining the appropriateness of using and distributing </FONT><FONT SIZE="2">the Program</FONT><FONT SIZE="2"> and assumes all risks associated with its exercise of rights under this Agreement</FONT><FONT SIZE="2">, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, </FONT><FONT SIZE="2">programs or equipment, and unavailability or interruption of operations</FONT><FONT SIZE="2">.  </FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2"><B>6.  DISCLAIMER OF LIABILITY</B></FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2">EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES </FONT><FONT SIZE="2">(INCLUDING WITHOUT LIMITATION LOST PROFITS),</FONT><FONT SIZE="2"> HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"><B>7.  GENERAL</B></FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2">If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.</FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed.  In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. </FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance.  If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable.  However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.  </FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2">Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted  and may only be modified in the following manner. The Agreement Steward reserves the right to </FONT><FONT SIZE="2">publish new versions (including revisions) of this Agreement from time to </FONT><FONT SIZE="2">time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward.   IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity.  </FONT><FONT SIZE="2">Each new version of the Agreement will be given a distinguishing version number.  The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new </FONT><FONT SIZE="2">version.  </FONT><FONT SIZE="2">Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, </FONT><FONT SIZE="2">by implication, estoppel or otherwise</FONT><FONT SIZE="2">.</FONT><FONT SIZE="2">  All rights in the Program not expressly granted under this Agreement are reserved.</FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose.  Each party waives its rights to a jury trial in any resulting litigation.</FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT>
+
+</BODY>
+
+</HTML>
\ No newline at end of file
diff --git a/test-runner/src/junit/runner/BaseTestRunner.java b/test-runner/src/junit/runner/BaseTestRunner.java
new file mode 100644
index 0000000..e7e0431
--- /dev/null
+++ b/test-runner/src/junit/runner/BaseTestRunner.java
@@ -0,0 +1,340 @@
+package junit.runner;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.text.NumberFormat;
+import java.util.Properties;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestListener;
+import junit.framework.TestSuite;
+
+/**
+ * Base class for all test runners.
+ * This class was born live on stage in Sardinia during XP2000.
+ */
+public abstract class BaseTestRunner implements TestListener {
+	public static final String SUITE_METHODNAME= "suite";
+
+	private static Properties fPreferences;
+	static int fgMaxMessageLength= 500;
+	static boolean fgFilterStack= true;
+	boolean fLoading= true;
+
+    /*
+    * Implementation of TestListener
+    */
+	public synchronized void startTest(Test test) {
+		testStarted(test.toString());
+	}
+
+	protected static void setPreferences(Properties preferences) {
+		fPreferences= preferences;
+	}
+
+	protected static Properties getPreferences() {
+		if (fPreferences == null) {
+			fPreferences= new Properties();
+	 		fPreferences.put("loading", "true");
+ 			fPreferences.put("filterstack", "true");
+  			readPreferences();
+		}
+		return fPreferences;
+	}
+
+	public static void savePreferences() throws IOException {
+		FileOutputStream fos= new FileOutputStream(getPreferencesFile());
+		try {
+			getPreferences().store(fos, "");
+		} finally {
+			fos.close();
+		}
+	}
+
+	// android-changed remove 'static' qualifier for API compatibility
+	public void setPreference(String key, String value) {
+		getPreferences().put(key, value);
+	}
+
+	public synchronized void endTest(Test test) {
+		testEnded(test.toString());
+	}
+
+	public synchronized void addError(final Test test, final Throwable t) {
+		testFailed(TestRunListener.STATUS_ERROR, test, t);
+	}
+
+	public synchronized void addFailure(final Test test, final AssertionFailedError t) {
+		testFailed(TestRunListener.STATUS_FAILURE, test, t);
+	}
+
+	// TestRunListener implementation
+
+	public abstract void testStarted(String testName);
+
+	public abstract void testEnded(String testName);
+
+	public abstract void testFailed(int status, Test test, Throwable t);
+
+	/**
+	 * Returns the Test corresponding to the given suite. This is
+	 * a template method, subclasses override runFailed(), clearStatus().
+	 */
+	public Test getTest(String suiteClassName) {
+		if (suiteClassName.length() <= 0) {
+			clearStatus();
+			return null;
+		}
+		Class<?> testClass= null;
+		try {
+			testClass= loadSuiteClass(suiteClassName);
+		} catch (ClassNotFoundException e) {
+			String clazz= e.getMessage();
+			if (clazz == null)
+				clazz= suiteClassName;
+			runFailed("Class not found \""+clazz+"\"");
+			return null;
+		} catch(Exception e) {
+			runFailed("Error: "+e.toString());
+			return null;
+		}
+		Method suiteMethod= null;
+		try {
+			suiteMethod= testClass.getMethod(SUITE_METHODNAME, new Class[0]);
+	 	} catch(Exception e) {
+	 		// try to extract a test suite automatically
+			clearStatus();
+			return new TestSuite(testClass);
+		}
+		if (! Modifier.isStatic(suiteMethod.getModifiers())) {
+			runFailed("Suite() method must be static");
+			return null;
+		}
+		Test test= null;
+		try {
+			test= (Test)suiteMethod.invoke(null, (Object[])new Class[0]); // static method
+			if (test == null)
+				return test;
+		}
+		catch (InvocationTargetException e) {
+			runFailed("Failed to invoke suite():" + e.getTargetException().toString());
+			return null;
+		}
+		catch (IllegalAccessException e) {
+			runFailed("Failed to invoke suite():" + e.toString());
+			return null;
+		}
+
+		clearStatus();
+		return test;
+	}
+
+	/**
+	 * Returns the formatted string of the elapsed time.
+	 */
+	public String elapsedTimeAsString(long runTime) {
+		return NumberFormat.getInstance().format((double)runTime/1000);
+	}
+
+	/**
+	 * Processes the command line arguments and
+	 * returns the name of the suite class to run or null
+	 */
+	protected String processArguments(String[] args) {
+		String suiteName= null;
+		for (int i= 0; i < args.length; i++) {
+			if (args[i].equals("-noloading")) {
+				setLoading(false);
+			} else if (args[i].equals("-nofilterstack")) {
+				fgFilterStack= false;
+			} else if (args[i].equals("-c")) {
+				if (args.length > i+1)
+					suiteName= extractClassName(args[i+1]);
+				else
+					System.out.println("Missing Test class name");
+				i++;
+			} else {
+				suiteName= args[i];
+			}
+		}
+		return suiteName;
+	}
+
+	/**
+	 * Sets the loading behaviour of the test runner
+	 */
+	public void setLoading(boolean enable) {
+		fLoading= enable;
+	}
+	/**
+	 * Extract the class name from a String in VA/Java style
+	 */
+	public String extractClassName(String className) {
+		if(className.startsWith("Default package for"))
+			return className.substring(className.lastIndexOf(".")+1);
+		return className;
+	}
+
+	/**
+	 * Truncates a String to the maximum length.
+	 */
+	public static String truncate(String s) {
+		if (fgMaxMessageLength != -1 && s.length() > fgMaxMessageLength)
+			s= s.substring(0, fgMaxMessageLength)+"...";
+		return s;
+	}
+
+	/**
+	 * Override to define how to handle a failed loading of
+	 * a test suite.
+	 */
+	protected abstract void runFailed(String message);
+
+	// BEGIN android-changed - add back getLoader() for API compatibility
+	/**
+	 * Returns the loader to be used.
+	 *
+	 * @deprecated not present in JUnit4.10
+	 */
+	public TestSuiteLoader getLoader() {
+		return new StandardTestSuiteLoader();
+	}
+	// END android-changed
+
+	/**
+	 * Returns the loaded Class for a suite name.
+	 */
+	protected Class<?> loadSuiteClass(String suiteClassName) throws ClassNotFoundException {
+		return Class.forName(suiteClassName);
+	}
+
+	/**
+	 * Clears the status message.
+	 */
+	protected void clearStatus() { // Belongs in the GUI TestRunner class
+	}
+
+	protected boolean useReloadingTestSuiteLoader() {
+		return getPreference("loading").equals("true") && fLoading;
+	}
+
+	private static File getPreferencesFile() {
+	 	String home= System.getProperty("user.home");
+ 		return new File(home, "junit.properties");
+ 	}
+
+ 	private static void readPreferences() {
+ 		InputStream is= null;
+ 		try {
+ 			is= new FileInputStream(getPreferencesFile());
+ 			setPreferences(new Properties(getPreferences()));
+			getPreferences().load(is);
+		} catch (IOException e) {
+			try {
+				if (is != null)
+					is.close();
+			} catch (IOException e1) {
+			}
+		}
+ 	}
+
+ 	public static String getPreference(String key) {
+ 		return getPreferences().getProperty(key);
+ 	}
+
+ 	public static int getPreference(String key, int dflt) {
+ 		String value= getPreference(key);
+ 		int intValue= dflt;
+ 		if (value == null)
+ 			return intValue;
+ 		try {
+ 			intValue= Integer.parseInt(value);
+ 	 	} catch (NumberFormatException ne) {
+ 		}
+ 		return intValue;
+ 	}
+
+	/**
+	 * Returns a filtered stack trace
+	 */
+	public static String getFilteredTrace(Throwable t) {
+		StringWriter stringWriter= new StringWriter();
+		PrintWriter writer= new PrintWriter(stringWriter);
+		t.printStackTrace(writer);
+		StringBuffer buffer= stringWriter.getBuffer();
+		String trace= buffer.toString();
+		return BaseTestRunner.getFilteredTrace(trace);
+	}
+
+	// BEGIN android-changed - add back this method for API compatibility
+	/** @deprecated not present in JUnit4.10 */
+	public static boolean inVAJava() {
+		return false;
+	}
+	// END android-changed
+
+	/**
+	 * Filters stack frames from internal JUnit classes
+	 */
+	public static String getFilteredTrace(String stack) {
+		if (showStackRaw())
+			return stack;
+
+		StringWriter sw= new StringWriter();
+		PrintWriter pw= new PrintWriter(sw);
+		StringReader sr= new StringReader(stack);
+		// BEGIN android-changed
+		// Use a sensible default buffer size
+		BufferedReader br= new BufferedReader(sr, 1000);
+		// END android-changed
+
+		String line;
+		try {
+			while ((line= br.readLine()) != null) {
+				if (!filterLine(line))
+					pw.println(line);
+			}
+		} catch (Exception IOException) {
+			return stack; // return the stack unfiltered
+		}
+		return sw.toString();
+	}
+
+	protected static boolean showStackRaw() {
+		return !getPreference("filterstack").equals("true") || fgFilterStack == false;
+	}
+
+	static boolean filterLine(String line) {
+		String[] patterns= new String[] {
+			"junit.framework.TestCase",
+			"junit.framework.TestResult",
+			"junit.framework.TestSuite",
+			"junit.framework.Assert.", // don't filter AssertionFailure
+			"junit.swingui.TestRunner",
+			"junit.awtui.TestRunner",
+			"junit.textui.TestRunner",
+			"java.lang.reflect.Method.invoke("
+		};
+		for (int i= 0; i < patterns.length; i++) {
+			if (line.indexOf(patterns[i]) > 0)
+				return true;
+		}
+		return false;
+	}
+
+ 	static {
+ 		fgMaxMessageLength= getPreference("maxmessage", fgMaxMessageLength);
+ 	}
+
+}
diff --git a/test-runner/src/junit/runner/StandardTestSuiteLoader.java b/test-runner/src/junit/runner/StandardTestSuiteLoader.java
new file mode 100644
index 0000000..808963a
--- /dev/null
+++ b/test-runner/src/junit/runner/StandardTestSuiteLoader.java
@@ -0,0 +1,23 @@
+package junit.runner;
+
+// android-changed - class not present in upstream JUnit 4.10
+// added here to retain BaseTestRunner.getLoader API
+
+/**
+ * The standard test suite loader. It can only load the same class once.
+ * {@hide}
+ */
+public class StandardTestSuiteLoader implements TestSuiteLoader {
+    /**
+     * Uses the system class loader to load the test class
+     */
+    public Class load(String suiteClassName) throws ClassNotFoundException {
+        return Class.forName(suiteClassName);
+    }
+    /**
+     * Uses the system class loader to load the test class
+     */
+    public Class reload(Class aClass) throws ClassNotFoundException {
+        return aClass;
+    }
+}
diff --git a/test-runner/src/junit/runner/TestRunListener.java b/test-runner/src/junit/runner/TestRunListener.java
new file mode 100644
index 0000000..0e95819
--- /dev/null
+++ b/test-runner/src/junit/runner/TestRunListener.java
@@ -0,0 +1,20 @@
+package junit.runner;
+/**
+ * A listener interface for observing the
+ * execution of a test run. Unlike TestListener,
+ * this interface using only primitive objects,
+ * making it suitable for remote test execution.
+ * {@hide} - Not needed for 1.0 SDK
+ */
+ public interface TestRunListener {
+     /* test status constants*/
+     public static final int STATUS_ERROR= 1;
+     public static final int STATUS_FAILURE= 2;
+
+     public void testRunStarted(String testSuiteName, int testCount);
+     public void testRunEnded(long elapsedTime);
+     public void testRunStopped(long elapsedTime);
+     public void testStarted(String testName);
+     public void testEnded(String testName);
+     public void testFailed(int status, String testName, String trace);
+}
diff --git a/test-runner/src/junit/runner/TestSuiteLoader.java b/test-runner/src/junit/runner/TestSuiteLoader.java
new file mode 100644
index 0000000..9cc6d81
--- /dev/null
+++ b/test-runner/src/junit/runner/TestSuiteLoader.java
@@ -0,0 +1,11 @@
+package junit.runner;
+
+/**
+ * An interface to define how a test suite should be loaded.
+ *
+ */
+//  TODO: deprecate
+public interface TestSuiteLoader {
+    abstract public Class load(String suiteClassName) throws ClassNotFoundException;
+    abstract public Class reload(Class aClass) throws ClassNotFoundException;
+}
diff --git a/test-runner/src/junit/runner/Version.java b/test-runner/src/junit/runner/Version.java
new file mode 100644
index 0000000..dd88c03
--- /dev/null
+++ b/test-runner/src/junit/runner/Version.java
@@ -0,0 +1,20 @@
+package junit.runner;
+
+/**
+ * This class defines the current version of JUnit
+ */
+public class Version {
+	private Version() {
+		// don't instantiate
+	}
+
+	public static String id() {
+		return "4.10";
+	}
+
+	// android-changed
+	/** @hide - not needed for public API */
+	public static void main(String[] args) {
+		System.out.println(id());
+	}
+}
diff --git a/test-runner/src/junit/runner/package-info.java b/test-runner/src/junit/runner/package-info.java
new file mode 100644
index 0000000..b746185
--- /dev/null
+++ b/test-runner/src/junit/runner/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Provides JUnit v3.x test runners.
+ */
+package junit.runner;
\ No newline at end of file
diff --git a/test-runner/src/junit/runner/package.html b/test-runner/src/junit/runner/package.html
new file mode 100644
index 0000000..f08fa70
--- /dev/null
+++ b/test-runner/src/junit/runner/package.html
@@ -0,0 +1,5 @@
+<HTML>
+<BODY>
+Utility classes supporting the junit test framework.
+</BODY>
+</HTML>
diff --git a/test-runner/src/junit/textui/ResultPrinter.java b/test-runner/src/junit/textui/ResultPrinter.java
new file mode 100644
index 0000000..b4914529
--- /dev/null
+++ b/test-runner/src/junit/textui/ResultPrinter.java
@@ -0,0 +1,144 @@
+
+package junit.textui;
+
+import java.io.PrintStream;
+// android-changed
+// The following line was removed for compatibility with Android libraries.
+// import java.text.NumberFormat;
+import java.util.Enumeration;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestFailure;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+import junit.runner.BaseTestRunner;
+
+public class ResultPrinter implements TestListener {
+	PrintStream fWriter;
+	int fColumn= 0;
+	
+	public ResultPrinter(PrintStream writer) {
+		fWriter= writer;
+	}
+	
+	/* API for use by textui.TestRunner
+	 */
+
+	synchronized void print(TestResult result, long runTime) {
+		printHeader(runTime);
+	    printErrors(result);
+	    printFailures(result);
+	    printFooter(result);
+	}
+
+	void printWaitPrompt() {
+		getWriter().println();
+		getWriter().println("<RETURN> to continue");
+	}
+	
+	/* Internal methods 
+	 */
+
+	protected void printHeader(long runTime) {
+		getWriter().println();
+		getWriter().println("Time: "+elapsedTimeAsString(runTime));
+	}
+	
+	protected void printErrors(TestResult result) {
+		printDefects(result.errors(), result.errorCount(), "error");
+	}
+	
+	protected void printFailures(TestResult result) {
+		printDefects(result.failures(), result.failureCount(), "failure");
+	}
+	
+	protected void printDefects(Enumeration<TestFailure> booBoos, int count, String type) {
+		if (count == 0) return;
+		if (count == 1)
+			getWriter().println("There was " + count + " " + type + ":");
+		else
+			getWriter().println("There were " + count + " " + type + "s:");
+		for (int i= 1; booBoos.hasMoreElements(); i++) {
+			printDefect(booBoos.nextElement(), i);
+		}
+	}
+	
+	public void printDefect(TestFailure booBoo, int count) { // only public for testing purposes
+		printDefectHeader(booBoo, count);
+		printDefectTrace(booBoo);
+	}
+
+	protected void printDefectHeader(TestFailure booBoo, int count) {
+		// I feel like making this a println, then adding a line giving the throwable a chance to print something
+		// before we get to the stack trace.
+		getWriter().print(count + ") " + booBoo.failedTest());
+	}
+
+	protected void printDefectTrace(TestFailure booBoo) {
+		getWriter().print(BaseTestRunner.getFilteredTrace(booBoo.trace()));
+	}
+
+	protected void printFooter(TestResult result) {
+		if (result.wasSuccessful()) {
+			getWriter().println();
+			getWriter().print("OK");
+			getWriter().println (" (" + result.runCount() + " test" + (result.runCount() == 1 ? "": "s") + ")");
+
+		} else {
+			getWriter().println();
+			getWriter().println("FAILURES!!!");
+			getWriter().println("Tests run: "+result.runCount()+ 
+				         ",  Failures: "+result.failureCount()+
+				         ",  Errors: "+result.errorCount());
+		}
+	    getWriter().println();
+	}
+
+
+	/**
+	 * Returns the formatted string of the elapsed time.
+	 * Duplicated from BaseTestRunner. Fix it.
+	 */
+	protected String elapsedTimeAsString(long runTime) {
+		// android-changed
+		// The following line was altered for compatibility with
+		// Android libraries.
+		return Double.toString((double)runTime/1000);
+	}
+
+	public PrintStream getWriter() {
+		return fWriter;
+	}
+	/**
+	 * @see junit.framework.TestListener#addError(Test, Throwable)
+	 */
+	public void addError(Test test, Throwable t) {
+		getWriter().print("E");
+	}
+
+	/**
+	 * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError)
+	 */
+	public void addFailure(Test test, AssertionFailedError t) {
+		getWriter().print("F");
+	}
+
+	/**
+	 * @see junit.framework.TestListener#endTest(Test)
+	 */
+	public void endTest(Test test) {
+	}
+
+	/**
+	 * @see junit.framework.TestListener#startTest(Test)
+	 */
+	public void startTest(Test test) {
+		getWriter().print(".");
+		if (fColumn++ >= 40) {
+			getWriter().println();
+			fColumn= 0;
+		}
+	}
+
+}
diff --git a/test-runner/src/junit/textui/TestRunner.java b/test-runner/src/junit/textui/TestRunner.java
new file mode 100644
index 0000000..046448e
--- /dev/null
+++ b/test-runner/src/junit/textui/TestRunner.java
@@ -0,0 +1,203 @@
+package junit.textui;
+
+
+import java.io.PrintStream;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+import junit.runner.BaseTestRunner;
+import junit.runner.Version;
+
+/**
+ * A command line based tool to run tests.
+ * <pre>
+ * java junit.textui.TestRunner [-wait] TestCaseClass
+ * </pre>
+ * 
+ * <p>TestRunner expects the name of a TestCase class as argument.
+ * If this class defines a static <code>suite</code> method it 
+ * will be invoked and the returned test is run. Otherwise all 
+ * the methods starting with "test" having no arguments are run.</p>
+ * 
+ * <p> When the wait command line argument is given TestRunner
+ * waits until the users types RETURN.</p>
+ * 
+ * <p>TestRunner prints a trace as the tests are executed followed by a
+ * summary at the end.</p>
+ */
+public class TestRunner extends BaseTestRunner {
+	private ResultPrinter fPrinter;
+	
+	public static final int SUCCESS_EXIT= 0;
+	public static final int FAILURE_EXIT= 1;
+	public static final int EXCEPTION_EXIT= 2;
+
+	/**
+	 * Constructs a TestRunner.
+	 */
+	public TestRunner() {
+		this(System.out);
+	}
+
+	/**
+	 * Constructs a TestRunner using the given stream for all the output
+	 */
+	public TestRunner(PrintStream writer) {
+		this(new ResultPrinter(writer));
+	}
+	
+	/**
+	 * Constructs a TestRunner using the given ResultPrinter all the output
+	 */
+	public TestRunner(ResultPrinter printer) {
+		fPrinter= printer;
+	}
+	
+	/**
+	 * Runs a suite extracted from a TestCase subclass.
+	 */
+	static public void run(Class<? extends TestCase> testClass) {
+		run(new TestSuite(testClass));
+	}
+
+	/**
+	 * Runs a single test and collects its results.
+	 * This method can be used to start a test run
+	 * from your program.
+	 * <pre>
+	 * public static void main (String[] args) {
+	 *    test.textui.TestRunner.run(suite());
+	 * }
+	 * </pre>
+	 */
+	static public TestResult run(Test test) {
+		TestRunner runner= new TestRunner();
+		return runner.doRun(test);
+	}
+
+	/**
+	 * Runs a single test and waits until the user
+	 * types RETURN.
+	 */
+	static public void runAndWait(Test suite) {
+		TestRunner aTestRunner= new TestRunner();
+		aTestRunner.doRun(suite, true);
+	}
+
+	@Override
+	public void testFailed(int status, Test test, Throwable t) {
+	}
+	
+	@Override
+	public void testStarted(String testName) {
+	}
+	
+	@Override
+	public void testEnded(String testName) {
+	}
+
+	/**
+	 * Creates the TestResult to be used for the test run.
+	 */
+	protected TestResult createTestResult() {
+		return new TestResult();
+	}
+	
+	public TestResult doRun(Test test) {
+		return doRun(test, false);
+	}
+	
+	public TestResult doRun(Test suite, boolean wait) {
+		TestResult result= createTestResult();
+		result.addListener(fPrinter);
+		long startTime= System.currentTimeMillis();
+		suite.run(result);
+		long endTime= System.currentTimeMillis();
+		long runTime= endTime-startTime;
+		fPrinter.print(result, runTime);
+
+		pause(wait);
+		return result;
+	}
+
+	protected void pause(boolean wait) {
+		if (!wait) return;
+		fPrinter.printWaitPrompt();
+		try {
+			System.in.read();
+		}
+		catch(Exception e) {
+		}
+	}
+	
+	public static void main(String args[]) {
+		TestRunner aTestRunner= new TestRunner();
+		try {
+			TestResult r= aTestRunner.start(args);
+			if (!r.wasSuccessful()) 
+				System.exit(FAILURE_EXIT);
+			System.exit(SUCCESS_EXIT);
+		} catch(Exception e) {
+			System.err.println(e.getMessage());
+			System.exit(EXCEPTION_EXIT);
+		}
+	}
+
+	/**
+	 * Starts a test run. Analyzes the command line arguments and runs the given
+	 * test suite.
+	 */
+	public TestResult start(String args[]) throws Exception {
+		String testCase= "";
+		String method= "";
+		boolean wait= false;
+
+		for (int i= 0; i < args.length; i++) {
+			if (args[i].equals("-wait"))
+				wait= true;
+			else if (args[i].equals("-c"))
+				testCase= extractClassName(args[++i]);
+			else if (args[i].equals("-m")) {
+				String arg= args[++i];
+				int lastIndex= arg.lastIndexOf('.');
+				testCase= arg.substring(0, lastIndex);
+				method= arg.substring(lastIndex + 1);
+			} else if (args[i].equals("-v"))
+				System.err.println("JUnit " + Version.id() + " by Kent Beck and Erich Gamma");
+			else
+				testCase= args[i];
+		}
+
+		if (testCase.equals(""))
+			throw new Exception("Usage: TestRunner [-wait] testCaseName, where name is the name of the TestCase class");
+
+		try {
+			if (!method.equals("")) 
+				return runSingleMethod(testCase, method, wait);
+			Test suite= getTest(testCase);
+			return doRun(suite, wait);
+		} catch (Exception e) {
+			throw new Exception("Could not create and run test suite: " + e);
+		}
+	}
+
+	protected TestResult runSingleMethod(String testCase, String method, boolean wait) throws Exception {
+		Class<? extends TestCase> testClass= loadSuiteClass(testCase).asSubclass(TestCase.class);
+		Test test= TestSuite.createTest(testClass, method);
+		return doRun(test, wait);
+	}
+
+	@Override
+	protected void runFailed(String message) {
+		System.err.println(message);
+		System.exit(FAILURE_EXIT);
+	}
+	
+	public void setPrinter(ResultPrinter printer) {
+		fPrinter= printer;
+	}
+		
+	
+}
\ No newline at end of file
diff --git a/test-runner/src/junit/textui/package-info.java b/test-runner/src/junit/textui/package-info.java
new file mode 100644
index 0000000..2dcc10c
--- /dev/null
+++ b/test-runner/src/junit/textui/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Provides JUnit v3.x command line based tool to run tests.
+ * {@hide}
+ */
+package junit.textui;
\ No newline at end of file
diff --git a/test-runner/src/junit/textui/package.html b/test-runner/src/junit/textui/package.html
new file mode 100644
index 0000000..723f2ae
--- /dev/null
+++ b/test-runner/src/junit/textui/package.html
@@ -0,0 +1,6 @@
+<HTML>
+<BODY>
+Utility classes supporting the junit test framework.
+{@hide} - Not needed for 1.0 SDK
+</BODY>
+</HTML>